<commit_before>/**
 * Copyright (c) 2013-2016 Memba Sarl. All rights reserved.
 * Sources at https://github.com/Memba
 */

/* jshint browser: true, jquery: true */
/* globals define: false, require: false */

(function (f, define) {
    'use strict';
    define([
        // We are not loading window.kendo
        './window.assert',
        './window.logger',
        './app.logger'
    ], f);
})(function () {

    'use strict';

    (function ($, undefined) {

        var app = window.app = window.app || {};
        var kendo = window.kendo;
        var logger = new window.Logger('app.theme');
        var STRING = 'string';
        var FUNCTION = 'function';
        var THEME = 'theme';
        var DEFAULT = 'flat';
        // Note: app.i18n is not yet loaded, so we need a duplicated list
        var ALL = ['black', 'blueopal', 'bootstrap', 'default', 'fiori', 'flat', 'highcontrast', 'material', 'materialblack',
            'metro', 'metroblack', 'moonlight', 'nova', 'office365', 'silver', 'uniform'];

        var localStorage; // = window.localStorage;
        // The following is necessary when localStorage is explicitly disabled in browser settings
        try { localStorage = window.localStorage; } catch (ex) {}

        app.theme = {

            /**
             * Load a theme
             * @param theme
             */
            load: function (theme) {
                var dfd = $.Deferred();
                var oldTheme = localStorage && localStorage.getItem(THEME);
                var loader;
                if (ALL.indexOf(theme) === -1) {
                    theme = DEFAULT;
                }
                if (typeof oldTheme === STRING && oldTheme !== theme) {
                    // See https://github.com/webpack/style-loader/issues/48
                    // See https://github.com/webpack/webpack/issues/924
                    // See https://github.com/webpack/webpack/issues/993
                    loader = require('../styles/app.theme.' + oldTheme + '.less');
                    loader(function (style) {
                        style.unuse();
                    });
                }
                loader = require('../styles/app.theme.' + theme + '.less');
                loader(function (style) {
                    style.use();
                    if (localStorage && !$.isArray(matches)) {
                        try {
                            localStorage.setItem(THEME, theme);
                        } catch (exception) {
                            // @see http://chrisberkhout.com/blog/localstorage-errors/
                            if (!window.DOMException || !(exception instanceof window.DOMException) || exception.code !== window.DOMException.QUOTA_EXCEEDED_ERR) {
                                throw exception;
                            }
                        }
                    }
                    // if (window.device && window.device.cordova) { // Phonegap
                    if (app.mobile && kendo.mobile && kendo.mobile.Application &&
                        app.mobile.application instanceof kendo.mobile.Application) {
                        app.mobile.application.skin(theme);
                    } else { // Web application
                        $(document.documentElement).removeClass('k-' + oldTheme).addClass('k-' + theme);
                    }
                    app.theme.updateCharts(theme);
                    logger.debug({
                        message: 'theme changed to ' + theme,
                        method: 'load'
                    });
                    dfd.resolve();
                });
                return dfd.promise();
            },

            /* Blocks are nested too deeply. */
            /* jshint -W073 */

            /**
             * Update dataviz charts with new theme
             * @see http://demos.telerik.com/kendo-ui/content/shared/js/theme-chooser.js
             * @param theme
             */
            updateCharts: function (theme) {
                var themable = ['Chart', 'TreeMap', 'Diagram', 'StockChart', 'Sparkline', 'RadialGauge', 'LinearGauge'];
                if (kendo.dataviz && $.type(theme) === STRING) {
                    for (var i = 0; i < themable.length; i++) {
                        // Set globally for new widgets
                        var widget = kendo.dataviz.ui[themable[i]];
                        if (widget) {
                            widget.fn.options.theme = theme;
                        }
                        // Redraw existing widgets
                        var elements = $(kendo.roleSelector(themable[i].toLowerCase()));
                        for (var j = 0; j < elements.length; j++) {
                            var instance = $(elements[j]).data('kendo' + themable[i]);
                            if (instance && $.type(instance.setOptions) === FUNCTION && $.type(instance.redraw) === FUNCTION) {
                                // instance.options.theme = theme;
                                instance.setOptions({ theme: theme });
                                instance.redraw();
                            }
                        }
                    }
                }
            },

            /* jshint +W073 */

            /**
             * Update QR Codes
             * QR Codes are not themable, so we need to set color and background
             * @param theme
             */
            updateQRCodes: function (theme) {
                // TODO
            },

            /**
             * Get/set theme name
             * @param theme
             */
            name: function (theme) {
                if ($.type(theme) === STRING) {
                    app.theme.load(theme);
                } else if (theme === undefined) {
                    if ($.isArray(matches) && matches.length === 2 && $.type(matches[1] === STRING)) {
                        return matches[1].trim().toLowerCase();
                    } else {
                        theme = localStorage && localStorage.getItem(THEME);
                        return ($.type(theme) === STRING) ? theme : DEFAULT;
                    }
                } else {
                    throw new TypeError('bad theme');
                }
            }

        };

        // find a match in querystring (embedded player)
        var matches = /theme=([^&]+)/.exec(window.location.search.substr(1));
        // get theme from match or from localstorage ur use DEFAULT
        var theme = app.theme.name();
        // load theme
        app.theme.load(theme);

    }(window.jQuery));

    return window.app;

}, typeof define === 'function' && define.amd ? define : function (_, f) { 'use strict'; f(); });
<commit_msg>Attempt to fix localStorage issue when browser runs in private mode https://github.com/jlchereau/Kidoju-Webapp/issues/181<commit_after>/**
 * Copyright (c) 2013-2016 Memba Sarl. All rights reserved.
 * Sources at https://github.com/Memba
 */

/* jshint browser: true, jquery: true */
/* globals define: false, require: false */

(function (f, define) {
    'use strict';
    define([
        // We are not loading window.kendo
        './window.assert',
        './window.logger',
        './app.logger'
    ], f);
})(function () {

    'use strict';

    (function ($, undefined) {

        var app = window.app = window.app || {};
        var kendo = window.kendo;
        var logger = new window.Logger('app.theme');
        var STRING = 'string';
        var FUNCTION = 'function';
        var THEME = 'theme';
        var DEFAULT = 'flat';
        // Note: app.i18n is not yet loaded, so we need a duplicated list
        var ALL = ['black', 'blueopal', 'bootstrap', 'default', 'fiori', 'flat', 'highcontrast', 'material', 'materialblack',
            'metro', 'metroblack', 'moonlight', 'nova', 'office365', 'silver', 'uniform'];

        var localStorage; // = window.localStorage;
        // The following is necessary when localStorage is explicitly disabled in browser settings
        try { localStorage = window.localStorage; } catch (ex) {}

        app.theme = {

            /**
             * Load a theme
             * @param theme
             */
            load: function (theme) {
                var dfd = $.Deferred();
                var oldTheme = localStorage && localStorage.getItem(THEME);
                var loader;
                if (ALL.indexOf(theme) === -1) {
                    theme = DEFAULT;
                }
                if (typeof oldTheme === STRING && oldTheme !== theme) {
                    // See https://github.com/webpack/style-loader/issues/48
                    // See https://github.com/webpack/webpack/issues/924
                    // See https://github.com/webpack/webpack/issues/993
                    loader = require('../styles/app.theme.' + oldTheme + '.less');
                    loader(function (style) {
                        style.unuse();
                    });
                }
                loader = require('../styles/app.theme.' + theme + '.less');
                loader(function (style) {
                    style.use();
                    if (localStorage && !$.isArray(matches)) {
                        try {
                            localStorage.setItem(THEME, theme);
                        } catch (exception) {
                            // A QoutaExceededError in raised in private browsing, which we do not care about
                            // @see https://github.com/jlchereau/Kidoju-Webapp/issues/181
                            // @see http://chrisberkhout.com/blog/localstorage-errors/
                            if (!window.DOMException || !(exception instanceof window.DOMException) || exception.code !== window.DOMException.QUOTA_EXCEEDED_ERR) {
                                throw exception;
                            }
                        }
                    }
                    // if (window.device && window.device.cordova) { // Phonegap
                    if (app.mobile && kendo.mobile && kendo.mobile.Application &&
                        app.mobile.application instanceof kendo.mobile.Application) {
                        app.mobile.application.skin(theme);
                    } else { // Web application
                        $(document.documentElement).removeClass('k-' + oldTheme).addClass('k-' + theme);
                    }
                    app.theme.updateCharts(theme);
                    logger.debug({
                        message: 'theme changed to ' + theme,
                        method: 'load'
                    });
                    dfd.resolve();
                });
                return dfd.promise();
            },

            /* Blocks are nested too deeply. */
            /* jshint -W073 */

            /**
             * Update dataviz charts with new theme
             * @see http://demos.telerik.com/kendo-ui/content/shared/js/theme-chooser.js
             * @param theme
             */
            updateCharts: function (theme) {
                var themable = ['Chart', 'TreeMap', 'Diagram', 'StockChart', 'Sparkline', 'RadialGauge', 'LinearGauge'];
                if (kendo.dataviz && $.type(theme) === STRING) {
                    for (var i = 0; i < themable.length; i++) {
                        // Set globally for new widgets
                        var widget = kendo.dataviz.ui[themable[i]];
                        if (widget) {
                            widget.fn.options.theme = theme;
                        }
                        // Redraw existing widgets
                        var elements = $(kendo.roleSelector(themable[i].toLowerCase()));
                        for (var j = 0; j < elements.length; j++) {
                            var instance = $(elements[j]).data('kendo' + themable[i]);
                            if (instance && $.type(instance.setOptions) === FUNCTION && $.type(instance.redraw) === FUNCTION) {
                                // instance.options.theme = theme;
                                instance.setOptions({ theme: theme });
                                instance.redraw();
                            }
                        }
                    }
                }
            },

            /* jshint +W073 */

            /**
             * Update QR Codes
             * QR Codes are not themable, so we need to set color and background
             * @param theme
             */
            updateQRCodes: function (theme) {
                // TODO
            },

            /**
             * Get/set theme name
             * @param theme
             */
            name: function (theme) {
                if ($.type(theme) === STRING) {
                    app.theme.load(theme);
                } else if (theme === undefined) {
                    if ($.isArray(matches) && matches.length === 2 && $.type(matches[1] === STRING)) {
                        return matches[1].trim().toLowerCase();
                    } else {
                        theme = localStorage && localStorage.getItem(THEME);
                        return ($.type(theme) === STRING) ? theme : DEFAULT;
                    }
                } else {
                    throw new TypeError('bad theme');
                }
            }

        };

        // find a match in querystring (embedded player)
        var matches = /theme=([^&]+)/.exec(window.location.search.substr(1));
        // get theme from match or from localstorage ur use DEFAULT
        var theme = app.theme.name();
        // load theme
        app.theme.load(theme);

    }(window.jQuery));

    return window.app;

}, typeof define === 'function' && define.amd ? define : function (_, f) { 'use strict'; f(); });
<|endoftext|>


<commit_before>var $     = require('jquery');

require('jquery-serialize-object');

$(function() {
  var Parse = require('parse');
  Parse.initialize("Z2l0Zn6NGrHCDoBPKUeD7Tf1fAUDaazQihQFqnL8", "kGPp7cydleuFbhKB4mrviTmbIjrbTjhxGP4dP7Ls");

  // provision the router instance
  var router = require('routers/admin_router').instance;

  // begin tracking hashChange
  require('backbone').history.start();
});
<commit_msg>fix admin.js<commit_after>var $     = require('jquery'),
    Handlebars = require('handlebars-runtime');

require('jquery-serialize-object');
Handlebars.registerPartial("editService", require('templates/edit_service'));

$(function() {
  var Parse = require('parse');
  Parse.initialize("Z2l0Zn6NGrHCDoBPKUeD7Tf1fAUDaazQihQFqnL8", "kGPp7cydleuFbhKB4mrviTmbIjrbTjhxGP4dP7Ls");

  // provision the router instance
  var router = require('routers/admin_router').instance;

  // begin tracking hashChange
  require('backbone').history.start();
});
<|endoftext|>


<commit_before>$(document)
		.ready(function() {

				$('.delete_benchmark').live('click',function(event) {
					var myBenchmark = this.id.split("_")[1];
					var myUrl = base_url + "benchmark/delete";
					var form_data = {
							kBenchmark: myBenchmark,
							ajax: 1
					};
					choice = confirm("Are you absolutely sure you want to delete this benchmark entry? This cannot be undone!");
					if (choice) {
						$.ajax({
							url: myUrl,
							type:'POST',
							data: form_data,
							success: function(data){
							$("#benchmark_" + myBenchmark).fadeOut();
						}
						});//end ajax

					}// end if(choice);
					}// end function(event)
				);// end click

					$(".benchmark-grade, .benchmark-comment").live('keydown',function(event){
						var my_id = $(this).parents("tr").attr("id").split("_");
						var myBenchmark = my_id[1];
						$("#save_" + myBenchmark).fadeOut(1000);
					});

					$(".benchmark-grade").live('change', function(event){
						var my_id = $(this).parents("tr").attr("id").split("_");
						update_benchmark(my_id);

					});

					$(".benchmark-comment").live('blur',function(event){
						var my_id = $(this).parents("tr").attr("id").split("_");
						update_benchmark(my_id);
					});

					$(".benchmark-fill-down").live('click',function(event){
						$(".benchmark-grade").each(function(){
							if($(this).val()==""){
							$(this).val("M");
							var my_id = $(this).parents("tr").attr("id").split("_");
							update_benchmark(my_id);
							}

						});
					});

			}
		);// end document-ready

function update_benchmark (my_id){
	var myBenchmark = my_id[1];
	var myStudent = my_id[2];
	var myTeach = my_id[3];
	var myYear = $("#year").val();
	var myTerm = $("#term").val();
	var myQuarter = $("#quarter").val();
	var myGrade = $("#g_" + myBenchmark).val();
	var myComment = $("#c_" + myBenchmark).val();
	var form_data = {
			kBenchmark: myBenchmark,
			kStudent: myStudent,
			kTeach: myTeach,
			grade: myGrade,
			comment: myComment,
			quarter: myQuarter,
			term: myTerm,
			year: myYear,
			ajax: 1
	};
	var myUrl = base_url + "student_benchmark/update";
	$.ajax({
		type: "post",
		url: myUrl,
		data: form_data,
		success: function(data){
			console.log(data);
			$("#save_" + myBenchmark).html(data).show();

		},
		error: function(data){
			console.log(data);

		}
	});

}
<commit_msg>minor UI changes to benchmark editing<commit_after>$(document)
		.ready(function() {

				$('.delete_benchmark').live('click',function(event) {
					var myBenchmark = this.id.split("_")[1];
					var myUrl = base_url + "benchmark/delete";
					var form_data = {
							kBenchmark: myBenchmark,
							ajax: 1
					};
					choice = confirm("Are you absolutely sure you want to delete this benchmark entry? This cannot be undone!");
					if (choice) {
						$.ajax({
							url: myUrl,
							type:'POST',
							data: form_data,
							success: function(data){
							$("#benchmark_" + myBenchmark).fadeOut();
						}
						});//end ajax

					}// end if(choice);
					}// end function(event)
				);// end click

					$(".benchmark-grade, .benchmark-comment").live('keydown',function(event){
						var my_id = $(this).parents("tr").attr("id").split("_");
						var myBenchmark = my_id[1];
						$("#save_" + my_id[1]).fadeOut(1000);
					});

					$(".benchmark-grade").blur(function(event){
						var my_id = $(this).parents("tr").attr("id").split("_");
						update_benchmark(my_id);
						console.log("change");
						$("#save_" + my_id[1]).fadeOut(1000);

					});

					$(".benchmark-comment").blur(function(event){
						var my_id = $(this).parents("tr").attr("id").split("_");
						update_benchmark(my_id);
						console.log("blur");
						$("#save_" + my_id[1]).fadeOut(1000);

					});

					$(".benchmark-fill-down").click(function(event){
						$(".benchmark-grade").each(function(){
							if($(this).val()==""){
							$(this).val("M");
							var my_id = $(this).parents("tr").attr("id").split("_");
							update_benchmark(my_id);
							console.log('filldown');
							$("#save_" + my_id[1]).fadeOut(1000);

							}

						});
					});

			}
		);// end document-ready

function update_benchmark (my_id){
	var myBenchmark = my_id[1];
	var myStudent = my_id[2];
	var myTeach = my_id[3];
	var myYear = $("#year").val();
	var myTerm = $("#term").val();
	var myQuarter = $("#quarter").val();
	var myGrade = $("#g_" + myBenchmark).val();
	var myComment = $("#c_" + myBenchmark).val();
	var form_data = {
			kBenchmark: myBenchmark,
			kStudent: myStudent,
			kTeach: myTeach,
			grade: myGrade,
			comment: myComment,
			quarter: myQuarter,
			term: myTerm,
			year: myYear,
			ajax: 1
	};
	var myUrl = base_url + "student_benchmark/update";
	$.ajax({
		type: "post",
		url: myUrl,
		data: form_data,
		success: function(data){
			$("#save_" + myBenchmark).html(data).show();
		},
		error: function(data){
			console.log(data);
			$("#save_" + myBenchmark).html(data).show();

		}
	});

}
<|endoftext|>


<commit_before>// Issues:
//  no comment handling within strings
//  no string concatenation
//  no variable values yet

// Grammar implemented here:
//  bibtex -> (string | preamble | comment | entry)*;
//  string -> '@STRING' '{' key_equals_value '}';
//  preamble -> '@PREAMBLE' '{' value '}';
//  comment -> '@COMMENT' '{' value '}';
//  entry -> '@' key '{' key ',' key_value_list '}';
//  key_value_list -> key_equals_value (',' key_equals_value)*;
//  key_equals_value -> key '=' value;
//  value -> value_quotes | value_braces | key;
//  value_quotes -> '"' .*? '"'; // not quite
//  value_braces -> '{' .*? '"'; // not quite
//
// Original: http://home.in.tum.de/~muehe/bibtex-js/src/bibtex_js.js
// Cloned from (vesion 31f067c): https://github.com/AusterweilLab/bibtex-js-apa
// Modified by: hfang
function BibtexParser() {
	this.pos = 0;
	this.input = "";

	this.entries = {};
	this.strings = {
		JAN: "January",
		FEB: "February",
		MAR: "March",
		APR: "April",
		MAY: "May",
		JUN: "June",
		JUL: "July",
		AUG: "August",
		SEP: "September",
		OCT: "October",
		NOV: "November",
		DEC: "December"
	};
	this.currentKey = "";
	this.currentEntry = "";

	this.setInput = function(t) {
		this.input = t;
	}

	this.getEntries = function() {
		return this.entries;
	}

	this.isWhitespace = function(s) {
		return (s == ' ' || s == '\r' || s == '\t' || s == '\n');
	}

	this.match = function(s) {
		this.skipWhitespace();
		if (this.input.substring(this.pos, this.pos+s.length) == s) {
			this.pos += s.length;
		} else {
			throw "Token mismatch, expected " + s + ", found " + this.input.substring(this.pos);
		}
		this.skipWhitespace();
	}

	this.tryMatch = function(s) {
		this.skipWhitespace();
		if (this.input.substring(this.pos, this.pos+s.length) == s) {
			return true;
		} else {
			return false;
		}
		this.skipWhitespace();
	}

	this.skipWhitespace = function() {
		while (this.isWhitespace(this.input[this.pos])) {
			this.pos++;
		}
		if (this.input[this.pos] == "%") {
			while(this.input[this.pos] != "\n") {
				this.pos++;
			}
			this.skipWhitespace();
		}
	}

	this.value_braces = function() {
		var bracecount = 0;
		this.match("{");
		var start = this.pos;
		while(true) {
			if (this.input[this.pos] == '}' && this.input[this.pos-1] != '\\') {
				if (bracecount > 0) {
					bracecount--;
				} else {
					var end = this.pos;
					this.match("}");
					return this.input.substring(start, end);
				}
			} else if (this.input[this.pos] == '{') {
				bracecount++;
			} else if (this.pos == this.input.length-1) {
				throw "Unterminated value";
			}
			this.pos++;
		}
	}

	this.value_quotes = function() {
		this.match('"');
		var start = this.pos;
		while(true) {
			if (this.input[this.pos] == '"' && this.input[this.pos-1] != '\\') {
				var end = this.pos;
				this.match('"');
				return this.input.substring(start, end);
			} else if (this.pos == this.input.length-1) {
				throw "Unterminated value:" + this.input.substring(start);
			}
			this.pos++;
		}
	}

	this.single_value = function() {
		var start = this.pos;
		if (this.tryMatch("{")) {
			return this.value_braces();
		} else if (this.tryMatch('"')) {
			return this.value_quotes();
		} else {
			var k = this.key();
			if (this.strings[k.toUpperCase()]) {
				return this.strings[k];
			} else if (k.match("^[0-9]+$")) {
				return k;
			} else {
				throw "Value expected:" + this.input.substring(start);
			}
		}
	}

	this.value = function() {
		var values = [];
		values.push(this.single_value());
		while (this.tryMatch("#")) {
			this.match("#");
			values.push(this.single_value());
		}
		return values.join("");
	}

	this.key = function() {
		var start = this.pos;
		while(true) {
			if (this.pos == this.input.length) {
				throw "Runaway key";
			}

			if (this.input[this.pos].match("[a-zA-Z0-9_:\\./-]")) {
				this.pos++
			} else {
				return this.input.substring(start, this.pos).toUpperCase();
			}
		}
	}

	this.key_equals_value = function() {
		var key = this.key();
		if (this.tryMatch("=")) {
			this.match("=");
			var val = this.value();
			return [ key, val ];
		} else {
			throw "... = value expected, equals sign missing:" + this.input.substring(this.pos);
		}
	}

	this.key_value_list = function() {
		var kv = this.key_equals_value();
		this.entries[this.currentEntry][kv[0]] = kv[1];
		while (this.tryMatch(",")) {
			this.match(",");
			// fixes problems with commas at the end of a list
			if (this.tryMatch("}")) {
				break;
			}
			kv = this.key_equals_value();
			this.entries[this.currentEntry][kv[0]] = kv[1];
		}
	}

	this.entry_body = function() {
		this.currentEntry = this.key();
		this.entries[this.currentEntry] = new Object();
		this.match(",");
		this.key_value_list();
	}

	this.directive = function () {
		this.match("@");
		return "@"+this.key();
	}

	this.string = function () {
		var kv = this.key_equals_value();
		this.strings[kv[0].toUpperCase()] = kv[1];
	}

	this.preamble = function() {
		this.value();
	}

	this.comment = function() {
		this.value(); // this is wrong
	}

	this.entry = function() {
		this.entry_body();
	}

	this.bibtex = function() {
		while(this.tryMatch("@")) {
			var d = this.directive().toUpperCase();
			this.match("{");
			if (d == "@STRING") {
				this.string();
			} else if (d == "@PREAMBLE") {
				this.preamble();
			} else if (d == "@COMMENT") {
				this.comment();
			} else {
				this.entry();
			}
			this.match("}");
		}
	}
}

function BibtexDisplay() {
	this.fixValue = function (value) {
		value = value.replace(/\\glqq\s?/g, "„");
		value = value.replace(/\\grqq\s?/g, '”');
		value = value.replace(/\\ /g, ' ');
		value = value.replace(/\\url/g, '');
		value = value.replace(/---/g, '—');
		value = value.replace(/{\\"a}/g, 'ä');
		value = value.replace(/\{\\"o\}/g, 'ö');
		value = value.replace(/{\\"u}/g, 'ü');
		value = value.replace(/{\\"A}/g, 'Ä');
		value = value.replace(/{\\"O}/g, 'Ö');
		value = value.replace(/{\\"U}/g, 'Ü');
		value = value.replace(/\\ss/g, 'ß');
		value = value.replace(/\{(.*?)\}/g, '$1');
		return value;
	}

	function reformat(entry) {
		var retEntry = entry;
		if (entry.hasOwnProperty("AUTHOR")) {
			var perAuthor = entry.AUTHOR.split("and");
			var authStr = "";
			for (var i = 0; i < perAuthor.length; i++) {
				//! this for-loop is modified by hfang

				var curAuth = perAuthor[i].split(",");

				if (curAuth.length == 1) {
					// Swap first name and last name
					authStr += curAuth[0].trim();
				} else if (curAuth.length == 2){
					authStr += curAuth[1].trim();
					authStr += " ";
					authStr += curAuth[0].trim();
				} else {
					throw "unable to parse curAuth: " + perAuthor[i];
				}

				if (i < (perAuthor.length - 2)) {
					authStr += ", ";
				} else if (i == (perAuthor.length - 2)) {
					authStr += ", and ";
				}
			}

			retEntry.AUTHOR = authStr;
		}

		if (entry.hasOwnProperty("JOURNAL")) {
			retEntry.JOURNAL = entry.JOURNAL.replace("\\","")
		}

		if (entry.hasOwnProperty("PAGES")) {
			retEntry.PAGES = entry.PAGES.replace("--", "-")
		}
		return retEntry;
	}

	this.displayBibtex = function(input, output) {
		// parse bibtex input
		var b = new BibtexParser();
		b.setInput(input);
		b.bibtex();

		// save old entries to remove them later
		var old = output.find("*");

		// iterate over bibTeX entries
		var entries = b.getEntries();
		//var entries = Object.keys(entriesObj);
		// sort by alph then year
		//! \note (hfang): I think it only sort by year, which is what we want.

		var queue = new PriorityQueue({
			comparator: function (a, b) {
				if (isNaN(a.YEAR)) {
					return -1;
				}
				else if (isNaN(b.YEAR)) {
					return 1;
				}
				return parseInt(b.YEAR) - parseInt(a.YEAR);
			}
		});

		for (var entryKey in entries) {
			if (entries.hasOwnProperty(entryKey)) {
				queue.queue(entries[entryKey]);
			}
		}

		while (queue.length > 0) {
			var entry = queue.dequeue();
			//remap authors so that initials come first
			entry = reformat(entry);
			// find template
			var tpl = $(".bibtex_template").clone().removeClass('bibtex_template');

			// find all keys in the entry
			var keys = [];
			for (var key in entry) {
				keys.push(key.toUpperCase());
			}

			// find all ifs and check them
			var removed = false;
			do {
				// find next if
				var conds = tpl.find(".if");
				if (conds.size() == 0) {
					break;
				}

				// check if
				var cond = conds.first();
				cond.removeClass("if");
				var ifTrue = true;
				var classList = cond.attr('class').split(' ');
				$.each(classList, function (index, cls) {
					if (keys.indexOf(cls.toUpperCase()) < 0) {
						ifTrue = false;
					}
					cond.removeClass(cls);
				});

				// remove false ifs
				if (!ifTrue) {
					cond.remove();
				}
			} while (true);

			// fill in remaining fields
			for (var index in keys) {
				var key = keys[index];
				var value = entry[key] || "";
				tpl.find("span:not(a)." + key.toLowerCase()).html(this.fixValue(value));
				tpl.find("a." + key.toLowerCase()).attr('href', this.fixValue(value));
			}

			output.append(tpl);
			tpl.show();
		}

		// remove old entries
		old.remove();

	}

}

function bibtex_js_draw() {
	$(".bibtex_template").hide();
	// (new BibtexDisplay()).displayBibtex($("#bibtex_input").text(), $("#bibtex_display"));

	//! \note Be very careful since load is async call.
	$("#bibtex_input_common").load("bib/common.bib", function() {
		for (var year = 2016; year >= 2010; year--) {
			//! \note: load is async call.
			//! See http://stackoverflow.com/questions/29199442/jquery-load-inside-a-for-loop-not-working.
			(function (year) {
				var bibtex_input_id = "#bibtex_input-" + String(year);
				var bibtex_display_id = "#bibtex_display-" + String(year);
				var bib_filename = "bib/" + String(year) + ".bib"
					$(bibtex_input_id).load(bib_filename, function() {
						text = $("#bibtex_input_common").text() + $(bibtex_input_id).text();
						(new BibtexDisplay()).displayBibtex(text, $(bibtex_display_id));
					});
			})(year);
		}
	});
}

// check whether or not jquery is present
if (typeof jQuery == 'undefined') {
	// an interesting idea is loading jquery here. this might be added
	// in the future.
	alert("Please include jquery in all pages using bibtex_js!");
} else {
	// draw bibtex when loaded
	$(document).ready(function () {
		// check for template, add default
		if ($(".bibtex_template").size() == 0) {
			$("body").append("

\n  \n    , \n  \n  \n  \n    (view online)\n  \n
\n
\n  \n

");
		}

		bibtex_js_draw();
	});
}
<commit_msg>fix bibtex_js<commit_after>// Issues:
//  no comment handling within strings
//  no string concatenation
//  no variable values yet

// Grammar implemented here:
//  bibtex -> (string | preamble | comment | entry)*;
//  string -> '@STRING' '{' key_equals_value '}';
//  preamble -> '@PREAMBLE' '{' value '}';
//  comment -> '@COMMENT' '{' value '}';
//  entry -> '@' key '{' key ',' key_value_list '}';
//  key_value_list -> key_equals_value (',' key_equals_value)*;
//  key_equals_value -> key '=' value;
//  value -> value_quotes | value_braces | key;
//  value_quotes -> '"' .*? '"'; // not quite
//  value_braces -> '{' .*? '"'; // not quite
//
// Original: http://home.in.tum.de/~muehe/bibtex-js/src/bibtex_js.js
// Cloned from (vesion 31f067c): https://github.com/AusterweilLab/bibtex-js-apa
// Modified by: hfang
function BibtexParser() {
	this.pos = 0;
	this.input = "";

	this.entries = {};
	this.strings = {
		JAN: "January",
		FEB: "February",
		MAR: "March",
		APR: "April",
		MAY: "May",
		JUN: "June",
		JUL: "July",
		AUG: "August",
		SEP: "September",
		OCT: "October",
		NOV: "November",
		DEC: "December"
	};
	this.currentKey = "";
	this.currentEntry = "";

	this.setInput = function(t) {
		this.input = t;
	}

	this.getEntries = function() {
		return this.entries;
	}

	this.isWhitespace = function(s) {
		return (s == ' ' || s == '\r' || s == '\t' || s == '\n');
	}

	this.match = function(s) {
		this.skipWhitespace();
		if (this.input.substring(this.pos, this.pos+s.length) == s) {
			this.pos += s.length;
		} else {
			throw "Token mismatch, expected " + s + ", found " + this.input.substring(this.pos);
		}
		this.skipWhitespace();
	}

	this.tryMatch = function(s) {
		this.skipWhitespace();
		if (this.input.substring(this.pos, this.pos+s.length) == s) {
			return true;
		} else {
			return false;
		}
		this.skipWhitespace();
	}

	this.skipWhitespace = function() {
		while (this.isWhitespace(this.input[this.pos])) {
			this.pos++;
		}
		if (this.input[this.pos] == "%") {
			while(this.input[this.pos] != "\n") {
				this.pos++;
			}
			this.skipWhitespace();
		}
	}

	this.value_braces = function() {
		var bracecount = 0;
		this.match("{");
		var start = this.pos;
		while(true) {
			if (this.input[this.pos] == '}' && this.input[this.pos-1] != '\\') {
				if (bracecount > 0) {
					bracecount--;
				} else {
					var end = this.pos;
					this.match("}");
					return this.input.substring(start, end);
				}
			} else if (this.input[this.pos] == '{') {
				bracecount++;
			} else if (this.pos == this.input.length-1) {
				throw "Unterminated value";
			}
			this.pos++;
		}
	}

	this.value_quotes = function() {
		this.match('"');
		var start = this.pos;
		while(true) {
			if (this.input[this.pos] == '"' && this.input[this.pos-1] != '\\') {
				var end = this.pos;
				this.match('"');
				return this.input.substring(start, end);
			} else if (this.pos == this.input.length-1) {
				throw "Unterminated value:" + this.input.substring(start);
			}
			this.pos++;
		}
	}

	this.single_value = function() {
		var start = this.pos;
		if (this.tryMatch("{")) {
			return this.value_braces();
		} else if (this.tryMatch('"')) {
			return this.value_quotes();
		} else {
			var k = this.key();
			if (this.strings[k.toUpperCase()]) {
				return this.strings[k];
			} else if (k.match("^[0-9]+$")) {
				return k;
			} else {
				throw "Value expected:" + this.input.substring(start);
			}
		}
	}

	this.value = function() {
		var values = [];
		values.push(this.single_value());
		while (this.tryMatch("#")) {
			this.match("#");
			values.push(this.single_value());
		}
		return values.join("");
	}

	this.key = function() {
		var start = this.pos;
		while(true) {
			if (this.pos == this.input.length) {
				throw "Runaway key";
			}

			if (this.input[this.pos].match("[a-zA-Z0-9_:\\./-]")) {
				this.pos++
			} else {
				return this.input.substring(start, this.pos).toUpperCase();
			}
		}
	}

	this.key_equals_value = function() {
		var key = this.key();
		if (this.tryMatch("=")) {
			this.match("=");
			var val = this.value();
			return [ key, val ];
		} else {
			throw "... = value expected, equals sign missing:" + this.input.substring(this.pos);
		}
	}

	this.key_value_list = function() {
		var kv = this.key_equals_value();
		this.entries[this.currentEntry][kv[0]] = kv[1];
		while (this.tryMatch(",")) {
			this.match(",");
			// fixes problems with commas at the end of a list
			if (this.tryMatch("}")) {
				break;
			}
			kv = this.key_equals_value();
			this.entries[this.currentEntry][kv[0]] = kv[1];
		}
	}

	this.entry_body = function() {
		this.currentEntry = this.key();
		this.entries[this.currentEntry] = new Object();
		this.match(",");
		this.key_value_list();
	}

	this.directive = function () {
		this.match("@");
		return "@"+this.key();
	}

	this.string = function () {
		var kv = this.key_equals_value();
		this.strings[kv[0].toUpperCase()] = kv[1];
	}

	this.preamble = function() {
		this.value();
	}

	this.comment = function() {
		this.value(); // this is wrong
	}

	this.entry = function() {
		this.entry_body();
	}

	this.bibtex = function() {
		while(this.tryMatch("@")) {
			var d = this.directive().toUpperCase();
			this.match("{");
			if (d == "@STRING") {
				this.string();
			} else if (d == "@PREAMBLE") {
				this.preamble();
			} else if (d == "@COMMENT") {
				this.comment();
			} else {
				this.entry();
			}
			this.match("}");
		}
	}
}

function BibtexDisplay() {
	this.fixValue = function (value) {
		value = value.replace(/\\glqq\s?/g, "„");
		value = value.replace(/\\grqq\s?/g, '”');
		value = value.replace(/\\ /g, ' ');
		value = value.replace(/\\url/g, '');
		value = value.replace(/---/g, '—');
		value = value.replace(/{\\"a}/g, 'ä');
		value = value.replace(/\{\\"o\}/g, 'ö');
		value = value.replace(/{\\"u}/g, 'ü');
		value = value.replace(/{\\"A}/g, 'Ä');
		value = value.replace(/{\\"O}/g, 'Ö');
		value = value.replace(/{\\"U}/g, 'Ü');
		value = value.replace(/\\ss/g, 'ß');
		value = value.replace(/\{(.*?)\}/g, '$1');
		return value;
	}

	function reformat(entry) {
		var retEntry = entry;
		if (entry.hasOwnProperty("AUTHOR")) {
			var perAuthor = entry.AUTHOR.split("and");
			var authStr = "";
			for (var i = 0; i < perAuthor.length; i++) {
				//! this for-loop is modified by hfang

				var curAuth = perAuthor[i].split(",");

				if (curAuth.length == 1) {
					// Swap first name and last name
					authStr += curAuth[0].trim();
				} else if (curAuth.length == 2){
					authStr += curAuth[1].trim();
					authStr += " ";
					authStr += curAuth[0].trim();
				} else {
					throw "unable to parse curAuth: " + perAuthor[i];
				}

				if (i < (perAuthor.length - 2)) {
					authStr += ", ";
				} else if (i == (perAuthor.length - 2)) {
					if (i == 0) {
						authStr += " and ";
					} else {
						authStr += ", and ";
					}
				}
			}

			retEntry.AUTHOR = authStr;
		}

		if (entry.hasOwnProperty("JOURNAL")) {
			retEntry.JOURNAL = entry.JOURNAL.replace("\\","")
		}

		if (entry.hasOwnProperty("PAGES")) {
			retEntry.PAGES = entry.PAGES.replace("--", "-")
		}
		return retEntry;
	}

	this.displayBibtex = function(input, output) {
		// parse bibtex input
		var b = new BibtexParser();
		b.setInput(input);
		b.bibtex();

		// save old entries to remove them later
		var old = output.find("*");

		// iterate over bibTeX entries
		var entries = b.getEntries();
		//var entries = Object.keys(entriesObj);
		// sort by alph then year
		//! \note (hfang): I think it only sort by year, which is what we want.

		var queue = new PriorityQueue({
			comparator: function (a, b) {
				if (isNaN(a.YEAR)) {
					return -1;
				}
				else if (isNaN(b.YEAR)) {
					return 1;
				}
				return parseInt(b.YEAR) - parseInt(a.YEAR);
			}
		});

		for (var entryKey in entries) {
			if (entries.hasOwnProperty(entryKey)) {
				queue.queue(entries[entryKey]);
			}
		}

		while (queue.length > 0) {
			var entry = queue.dequeue();
			//remap authors so that initials come first
			entry = reformat(entry);
			// find template
			var tpl = $(".bibtex_template").clone().removeClass('bibtex_template');

			// find all keys in the entry
			var keys = [];
			for (var key in entry) {
				keys.push(key.toUpperCase());
			}

			// find all ifs and check them
			var removed = false;
			do {
				// find next if
				var conds = tpl.find(".if");
				if (conds.size() == 0) {
					break;
				}

				// check if
				var cond = conds.first();
				cond.removeClass("if");
				var ifTrue = true;
				var classList = cond.attr('class').split(' ');
				$.each(classList, function (index, cls) {
					if (keys.indexOf(cls.toUpperCase()) < 0) {
						ifTrue = false;
					}
					cond.removeClass(cls);
				});

				// remove false ifs
				if (!ifTrue) {
					cond.remove();
				}
			} while (true);

			// fill in remaining fields
			for (var index in keys) {
				var key = keys[index];
				var value = entry[key] || "";
				tpl.find("span:not(a)." + key.toLowerCase()).html(this.fixValue(value));
				tpl.find("a." + key.toLowerCase()).attr('href', this.fixValue(value));
			}

			output.append(tpl);
			tpl.show();
		}

		// remove old entries
		old.remove();

	}

}

function bibtex_js_draw() {
	$(".bibtex_template").hide();
	// (new BibtexDisplay()).displayBibtex($("#bibtex_input").text(), $("#bibtex_display"));

	//! \note Be very careful since load is async call.
	$("#bibtex_input_common").load("bib/common.bib", function() {
		for (var year = 2016; year >= 2010; year--) {
			//! \note: load is async call.
			//! See http://stackoverflow.com/questions/29199442/jquery-load-inside-a-for-loop-not-working.
			(function (year) {
				var bibtex_input_id = "#bibtex_input-" + String(year);
				var bibtex_display_id = "#bibtex_display-" + String(year);
				var bib_filename = "bib/" + String(year) + ".bib"
					$(bibtex_input_id).load(bib_filename, function() {
						text = $("#bibtex_input_common").text() + $(bibtex_input_id).text();
						(new BibtexDisplay()).displayBibtex(text, $(bibtex_display_id));
					});
			})(year);
		}
	});
}

// check whether or not jquery is present
if (typeof jQuery == 'undefined') {
	// an interesting idea is loading jquery here. this might be added
	// in the future.
	alert("Please include jquery in all pages using bibtex_js!");
} else {
	// draw bibtex when loaded
	$(document).ready(function () {
		// check for template, add default
		if ($(".bibtex_template").size() == 0) {
			$("body").append("

\n  \n    , \n  \n  \n  \n    (view online)\n  \n
\n
\n  \n

");
		}

		bibtex_js_draw();
	});
}
<|endoftext|>


<commit_before>// @flow
import RestClient from './RestClient';
import Evaluate from './Evaluate';

import Events from './Events';

import Emitter from 'tiny-emitter';
import Cookies from 'js-cookie';

const DEFAULT_BASE_URL = 'https://app.featureflow.io';
const DEFAULT_EVENTS_URL = 'https://events.featureflow.io';
const DEFAULT_RTM_URL = 'https://rtm.featureflow.io';

const DEFAULT_CONFIG: ConfigType = {
  baseUrl: DEFAULT_BASE_URL,
  eventsUrl: DEFAULT_EVENTS_URL,
  rtmUrl: DEFAULT_RTM_URL,
  streaming: true,
  defaultFeatures: {},
  useCookies: true,
  offline: true
};

const INIT_MODULE_ERROR = new Error('init() has not been called with a valid apiKey');

function loadFeatures(apiKey: string, userId: string): FeaturesType{
  try{
    return JSON.parse(localStorage.getItem(`ff:${userId}:${apiKey}`) || '{}');
  }
  catch(err){
    return {};
  }
}

function saveFeatures(apiKey: string, userId: string, features: FeaturesType): void{
  return localStorage.setItem(`ff:${userId}:${apiKey}`, JSON.stringify(features));
}

export default class FeatureflowClient{
  apiKey: string;
  features: FeaturesType;
  config: ConfigType;
  user: UserType;
  emitter: Emitter;
  on: (string)=>any;
  off: (string)=>any;
  receivedInitialResponse: boolean;

  constructor(apiKey: string, user: UserTypeParam = {}, config: ConfigTypeParam = {}, callback: NodeCallbackType<*> = ()=>{}){

    //if we are offline then just return the default

    this.receivedInitialResponse = false;
    this.emitter = new Emitter();
    this.apiKey = apiKey;

    //1. They must have an api key
    if (!this.apiKey){
      throw INIT_MODULE_ERROR;
    }

    //2. Extend the default configuration
    this.config = {
      ...DEFAULT_CONFIG,
      ...config
    };

    //3. Load initial data
    this.updateUser(user);

    //4. Set up realtime streaming
    if (!this.config.offline && this.config.streaming){
      let es = new window.EventSource(`${this.config.rtmUrl}/api/js/v1/stream/${this.apiKey}`);
      es.onmessage = (e) => {
        let keys = [];
        try{
          keys = JSON.parse(e.data);
        }
        catch(err){}

        RestClient.getFeatures(this.config.baseUrl, this.apiKey, this.user, keys, (error, features)=>{
          if (!error){
            this.features = {
              ...this.features,
              ...features
            };
            saveFeatures(this.apiKey, this.user.id, this.features);
            this.emitter.emit(Events.UPDATED_FEATURE, features);
            callback(undefined, features);
          }
          else{
            this.emitter.emit(Events.ERROR, error);
            callback(error);
          }
        })
      };
    }
    //Bind event emitter
    this.on = this.emitter.on.bind(this.emitter);
    this.off = this.emitter.off.bind(this.emitter);
  }

  updateUser(user: UserTypeParam = {}, callback: NodeCallbackType<*> = ()=>{}): void{
    //these could be event or session attributes ie not persisted directly to user but added to a separate attributes map
    const featureflowAttributes = {
    };
    const attributes = {
        ...user.attributes,
        ...featureflowAttributes
    };
    this.user = {
      id: user.id || this.getAnonymousId(),
      attributes: attributes
    };

    this.features = loadFeatures(this.apiKey, this.user.id);
    // Put this in timeout so we can listen to all events before it is returned
    setTimeout(()=>{
      this.emitter.emit(Events.LOADED_FROM_CACHE, this.features);

        if(this.config.offline) {
            setTimeout(()=> {
                this.features = this.config.defaultFeatures;
                saveFeatures(this.apiKey, this.user.id, this.features);
                this.emitter.emit(Events.INIT, this.features);
                this.emitter.emit(Events.LOADED, this.features);
                callback(undefined, this.features);
                return this.user;
            });
        }else{
            RestClient.getFeatures(this.config.baseUrl, this.apiKey, this.user, [], (error, features)=>{
                this.receivedInitialResponse = true;
                if (!error){
                    this.features = features || {};
                    saveFeatures(this.apiKey, this.user.id, this.features);
                    this.emitter.emit(Events.INIT, features);
                    this.emitter.emit(Events.LOADED, features);
                    callback(undefined, features);
                }
                else{
                    this.emitter.emit(Events.ERROR, error);
                    callback(error);
                }
                return this.user;
            });
        }
    },0);
  }
  getFeatures(): FeaturesType{
    if(this.config.offline){
        return this.config.defaultFeatures;
    }
    return this.features;
  }
  getUser(): UserType{
    return this.user;
  }
  evaluate(key: string) : Evaluate {
    if(this.config.offline){
      const evaluate = new Evaluate(this.config.defaultFeatures[key] || 'off');
      return evaluate
    }
    const evaluate = new Evaluate(this.features[key] || this.config.defaultFeatures[key] || 'off');
    RestClient.postEvaluateEvent(this.config.eventsUrl, this.apiKey, this.user, key, evaluate.value(), ()=>{});
    return evaluate;
  }

  goal(goal:string): void {
    if(this.config.offline) return;
    return RestClient.postGoalEvent(this.config.eventsUrl, this.apiKey, this.user, goal, this.getFeatures(),()=>{});
  }

  getAnonymousId(): string{
    return localStorage.getItem(`ff-anonymous-id`) || this.resetAnonymousId();
  }

  resetAnonymousId(): string{
    let anonymousId = 'anonymous:'+Math.random().toString(36).substring(2);
    localStorage.setItem(`ff-anonymous-id`, anonymousId);

    if (this.config.useCookies){
      //Set the anonymous key cookie for potential future usage with Server SDK
      Cookies.set('ff-anonymous-id', anonymousId);
    }
    return anonymousId;
  }
  hasReceivedInitialResponse(): boolean{
    return this.receivedInitialResponse;
  }
}
<commit_msg>offline default<commit_after>// @flow
import RestClient from './RestClient';
import Evaluate from './Evaluate';

import Events from './Events';

import Emitter from 'tiny-emitter';
import Cookies from 'js-cookie';

const DEFAULT_BASE_URL = 'https://app.featureflow.io';
const DEFAULT_EVENTS_URL = 'https://events.featureflow.io';
const DEFAULT_RTM_URL = 'https://rtm.featureflow.io';

const DEFAULT_CONFIG: ConfigType = {
  baseUrl: DEFAULT_BASE_URL,
  eventsUrl: DEFAULT_EVENTS_URL,
  rtmUrl: DEFAULT_RTM_URL,
  streaming: true,
  defaultFeatures: {},
  useCookies: true,
  offline: false
};

const INIT_MODULE_ERROR = new Error('init() has not been called with a valid apiKey');

function loadFeatures(apiKey: string, userId: string): FeaturesType{
  try{
    return JSON.parse(localStorage.getItem(`ff:${userId}:${apiKey}`) || '{}');
  }
  catch(err){
    return {};
  }
}

function saveFeatures(apiKey: string, userId: string, features: FeaturesType): void{
  return localStorage.setItem(`ff:${userId}:${apiKey}`, JSON.stringify(features));
}

export default class FeatureflowClient{
  apiKey: string;
  features: FeaturesType;
  config: ConfigType;
  user: UserType;
  emitter: Emitter;
  on: (string)=>any;
  off: (string)=>any;
  receivedInitialResponse: boolean;

  constructor(apiKey: string, user: UserTypeParam = {}, config: ConfigTypeParam = {}, callback: NodeCallbackType<*> = ()=>{}){

    //if we are offline then just return the default

    this.receivedInitialResponse = false;
    this.emitter = new Emitter();
    this.apiKey = apiKey;

    //1. They must have an api key
    if (!this.apiKey){
      throw INIT_MODULE_ERROR;
    }

    //2. Extend the default configuration
    this.config = {
      ...DEFAULT_CONFIG,
      ...config
    };

    //3. Load initial data
    this.updateUser(user);

    //4. Set up realtime streaming
    if (!this.config.offline && this.config.streaming){
      let es = new window.EventSource(`${this.config.rtmUrl}/api/js/v1/stream/${this.apiKey}`);
      es.onmessage = (e) => {
        let keys = [];
        try{
          keys = JSON.parse(e.data);
        }
        catch(err){}

        RestClient.getFeatures(this.config.baseUrl, this.apiKey, this.user, keys, (error, features)=>{
          if (!error){
            this.features = {
              ...this.features,
              ...features
            };
            saveFeatures(this.apiKey, this.user.id, this.features);
            this.emitter.emit(Events.UPDATED_FEATURE, features);
            callback(undefined, features);
          }
          else{
            this.emitter.emit(Events.ERROR, error);
            callback(error);
          }
        })
      };
    }
    //Bind event emitter
    this.on = this.emitter.on.bind(this.emitter);
    this.off = this.emitter.off.bind(this.emitter);
  }

  updateUser(user: UserTypeParam = {}, callback: NodeCallbackType<*> = ()=>{}): void{
    //these could be event or session attributes ie not persisted directly to user but added to a separate attributes map
    const featureflowAttributes = {
    };
    const attributes = {
        ...user.attributes,
        ...featureflowAttributes
    };
    this.user = {
      id: user.id || this.getAnonymousId(),
      attributes: attributes
    };

    this.features = loadFeatures(this.apiKey, this.user.id);
    // Put this in timeout so we can listen to all events before it is returned
    setTimeout(()=>{
      this.emitter.emit(Events.LOADED_FROM_CACHE, this.features);

        if(this.config.offline) {
            setTimeout(()=> {
                this.features = this.config.defaultFeatures;
                saveFeatures(this.apiKey, this.user.id, this.features);
                this.emitter.emit(Events.INIT, this.features);
                this.emitter.emit(Events.LOADED, this.features);
                callback(undefined, this.features);
                return this.user;
            });
        }else{
            RestClient.getFeatures(this.config.baseUrl, this.apiKey, this.user, [], (error, features)=>{
                this.receivedInitialResponse = true;
                if (!error){
                    this.features = features || {};
                    saveFeatures(this.apiKey, this.user.id, this.features);
                    this.emitter.emit(Events.INIT, features);
                    this.emitter.emit(Events.LOADED, features);
                    callback(undefined, features);
                }
                else{
                    this.emitter.emit(Events.ERROR, error);
                    callback(error);
                }
                return this.user;
            });
        }
    },0);
  }
  getFeatures(): FeaturesType{
    if(this.config.offline){
        return this.config.defaultFeatures;
    }
    return this.features;
  }
  getUser(): UserType{
    return this.user;
  }
  evaluate(key: string) : Evaluate {
    if(this.config.offline){
      const evaluate = new Evaluate(this.config.defaultFeatures[key] || 'off');
      return evaluate
    }
    const evaluate = new Evaluate(this.features[key] || this.config.defaultFeatures[key] || 'off');
    RestClient.postEvaluateEvent(this.config.eventsUrl, this.apiKey, this.user, key, evaluate.value(), ()=>{});
    return evaluate;
  }

  goal(goal:string): void {
    if(this.config.offline) return;
    return RestClient.postGoalEvent(this.config.eventsUrl, this.apiKey, this.user, goal, this.getFeatures(),()=>{});
  }

  getAnonymousId(): string{
    return localStorage.getItem(`ff-anonymous-id`) || this.resetAnonymousId();
  }

  resetAnonymousId(): string{
    let anonymousId = 'anonymous:'+Math.random().toString(36).substring(2);
    localStorage.setItem(`ff-anonymous-id`, anonymousId);

    if (this.config.useCookies){
      //Set the anonymous key cookie for potential future usage with Server SDK
      Cookies.set('ff-anonymous-id', anonymousId);
    }
    return anonymousId;
  }
  hasReceivedInitialResponse(): boolean{
    return this.receivedInitialResponse;
  }
}
<|endoftext|>


<commit_before>import _merge from 'lodash/merge'
import {darken} from 'polished'
import PropTypes from 'prop-types'
import {useMemo} from 'react'
import {ThemeProvider, withTheme, createGlobalStyle} from 'styled-components'
import {ToccoTheme} from 'tocco-theme'
import {theme} from 'tocco-ui'

const GlobalStyle = createGlobalStyle`
  // Legacy (display expression) icons styling
  .text-success {
    color: ${theme.color('signal.success.text')};
  }

  .text-muted {
    color: ${({theme}) => darken(0.4, theme.colors.paper)};
  }
`

const ThemeWrapper = ({defaultTheme, customTheme, children, theme}) => {
  const mergedTheme = useMemo(() => _merge({}, defaultTheme, customTheme), [defaultTheme, customTheme])

  return (
    <ThemeProvider theme={theme || mergedTheme}>
      <GlobalStyle />
      {children}
    </ThemeProvider>
  )
}

ThemeWrapper.defaultProps = {
  defaultTheme: ToccoTheme
}

ThemeWrapper.propTypes = {
  customTheme: PropTypes.object,
  defaultTheme: PropTypes.object,
  theme: PropTypes.object,
  children: PropTypes.node
}

export default withTheme(ThemeWrapper)
<commit_msg>feat(app-extensions): add css classes for display expressions styling<commit_after>import _merge from 'lodash/merge'
import {darken} from 'polished'
import PropTypes from 'prop-types'
import {useMemo} from 'react'
import {ThemeProvider, withTheme, createGlobalStyle} from 'styled-components'
import {ToccoTheme} from 'tocco-theme'
import {theme} from 'tocco-ui'

const GlobalStyle = createGlobalStyle`
  // Legacy (display expression) icons styling
  .text-success {
    color: ${theme.color('signal.success.text')};
  }

  .text-info {
    color: ${theme.color('signal.info.text')};
  }

  .text-warning {
    color: ${theme.color('signal.warning.text')};
  }

  .text-danger {
    color: ${theme.color('signal.danger.text')};
  }

  .text-muted {
    color: ${({theme}) => darken(0.4, theme.colors.paper)};
  }
`

const ThemeWrapper = ({defaultTheme, customTheme, children, theme}) => {
  const mergedTheme = useMemo(() => _merge({}, defaultTheme, customTheme), [defaultTheme, customTheme])

  return (
    <ThemeProvider theme={theme || mergedTheme}>
      <GlobalStyle />
      {children}
    </ThemeProvider>
  )
}

ThemeWrapper.defaultProps = {
  defaultTheme: ToccoTheme
}

ThemeWrapper.propTypes = {
  customTheme: PropTypes.object,
  defaultTheme: PropTypes.object,
  theme: PropTypes.object,
  children: PropTypes.node
}

export default withTheme(ThemeWrapper)
<|endoftext|>


<commit_before>const fs = require('fs');
const mkdirp = require('mkdirp');
const Generator = require('yeoman-generator');

class AppGenerator extends Generator {

    constructor(args, opts) {
        super(args, opts);

        this.option('spa');
        this.option('yarn');

        this.argument('projectName', { type: String, required: true });
    }

    /**
     * INITIALIZING
     *
     * @returns {undefined}
     */
    initializing() {
        this.log(`1. Creating project folder (${this.options.projectName}) ...`);
        this._createProjectFolder();
    }

    /**
     * PROMTING
     *
     * @returns {undefined}
     */
    prompting() {
        const prompts = [];

        if (this.options.spa) {
            prompts.push({
                type: 'input',
                name: 'appName',
                message: 'The name of your app (used in the manifest)',
                default: this.options.projectName
            });
            prompts.push({
                type: 'input',
                name: 'appNameShort',
                message: 'A short name for use as the text on the users home screen',
                default: this.options.projectName
            });
        }

        return this.prompt(prompts).then((answers) => {
            if (this.options.spa) {
                this._appName = answers.appName;
                this._appNameShort = answers.appNameShort;
            }
        });
    }

    /**
     * CONFIGURING
     *
     * @returns {undefined}
     */
    configuring() {
        this.log('2. Spawn dot files ...');
        this._copyStaticFiles('shared/dot');
    }

    /**
     * WRITING
     *
     * @returns {undefined}
     */
    writing() {
        this.log('3. Spawn initial project files ...');
        this._copySharedFiles();

        if (this.options.spa) {
            this._copySpaFiles();
        } else {
            this._copyBaseFiles();
        }
    }

    /**
     * INSTALL
     *
     * @returns {undefined}
     */
    install() {
        this.log('4. Installing dependencies ...');
        this._packagesInstall();
        this._devPackagesInstall();
    }

    /**
     * END
     *
     * @returns {undefined}
     */
    end() {
        this.log('5. DONE - Awesome!');
    }

    /**
     * CREATE PROJECT FOLDER
     *
     * @returns {undefined}
     * @private
     */
    _createProjectFolder() {
        mkdirp(this.options.projectName);
        this.destinationRoot(this.destinationPath(this.options.projectName));
    }

    _copySharedFiles() {
        this._copyStaticFiles('shared/static');
    }

    /**
     * COPY BASE FILES
     *
     * @returns {undefined}
     * @private
     */
    _copyBaseFiles() {
        this._copyStaticFiles('base/static');

        // Dynamic Templates
        this.fs.copyTpl(
            this.templatePath('base/dynamic/package.json'),
            this.destinationPath('package.json'),
            {
                projectName: this.options.projectName
            }
        );
    }

    /**
     * COPY SPA FILES
     * Files only needed for Single Page Applications
     *
     * @returns {undefined}
     * @private
     */
    _copySpaFiles() {
        this._copyStaticFiles('spa/static');

        // Dynamic Templates
        this.fs.copyTpl(
            this.templatePath('spa/dynamic/package.json'),
            this.destinationPath('package.json'),
            {
                projectName: this.options.projectName
            }
        );

        this.fs.copyTpl(
            this.templatePath('spa/dynamic/manifest.json'),
            this.destinationPath('public/manifest.json'),
            {
                appName: this._appName,
                appNameShort: this._appNameShort
            }
        );
    }

    /**
     * COPY STATIC TEMPLATES
     *
     * @param {string} path - destination to copy templates
     * @returns {undefined}
     * @private
     */
    _copyStaticFiles(path) {
        fs.readdir(this.templatePath(path), (err, files) => {
            if (err) this.log(err);

            files.forEach((file) => {
                this.fs.copy(
                    this.templatePath(`${path}/${file}`),
                    this.destinationPath(file)
                );
            });
        });
    }

    /**
     * Install packages
     *
     * @returns {undefined}
     * @private
     */
    _packagesInstall() {
        const dependencies = [
            'react',
            'react-dom',
            'sass-mq'
        ];

        if (this.options.spa) {
            dependencies.push('cerebral@beta');
            dependencies.push('@cerebral/router');
        }

        if (this.options.yarn) {
            this.yarnInstall(dependencies);
        } else {
            this.npmInstall(dependencies, { 'save': true });
        }
    }

    /**
     * Install dev-packages
     *
     * @returns {undefined}
     * @private
     */
    _devPackagesInstall() {
        const devDependencies = [
            'eslint',
            'eslint-plugin-import',
            'eslint-plugin-promise',
            'eslint-plugin-standard',
            'eslint-plugin-react',
            'eslint-plugin-jsx-a11y',
            'eslint-config-emakinacee-base',
            'eslint-config-emakinacee-react',
            'react-scripts',
            'node-sass-chokidar',
            'npm-run-all',
            'serve'
        ];

        if (this.options.yarn) {
            this.yarnInstall(devDependencies, { 'dev': true });
        } else {
            this.npmInstall(devDependencies, { 'save-dev': true });
        }
    }
};

module.exports = AppGenerator;
<commit_msg>[generator-emakinacee-react] change prompt text<commit_after>const fs = require('fs');
const mkdirp = require('mkdirp');
const Generator = require('yeoman-generator');

class AppGenerator extends Generator {

    constructor(args, opts) {
        super(args, opts);

        this.option('spa');
        this.option('yarn');

        this.argument('projectName', { type: String, required: true });
    }

    /**
     * INITIALIZING
     *
     * @returns {undefined}
     */
    initializing() {
        this.log(`1. Creating project folder (${this.options.projectName}) ...`);
        this._createProjectFolder();
    }

    /**
     * PROMTING
     *
     * @returns {undefined}
     */
    prompting() {
        const prompts = [];

        if (this.options.spa) {
            prompts.push({
                type: 'input',
                name: 'appName',
                message: 'The name of your app used in the manifest',
                default: this.options.projectName
            });
            prompts.push({
                type: 'input',
                name: 'appNameShort',
                message: 'A short name for use as the text on the users home screen',
                default: this.options.projectName
            });
        }

        return this.prompt(prompts).then((answers) => {
            if (this.options.spa) {
                this._appName = answers.appName;
                this._appNameShort = answers.appNameShort;
            }
        });
    }

    /**
     * CONFIGURING
     *
     * @returns {undefined}
     */
    configuring() {
        this.log('2. Spawn dot files ...');
        this._copyStaticFiles('shared/dot');
    }

    /**
     * WRITING
     *
     * @returns {undefined}
     */
    writing() {
        this.log('3. Spawn initial project files ...');
        this._copySharedFiles();

        if (this.options.spa) {
            this._copySpaFiles();
        } else {
            this._copyBaseFiles();
        }
    }

    /**
     * INSTALL
     *
     * @returns {undefined}
     */
    install() {
        this.log('4. Installing dependencies ...');
        this._packagesInstall();
        this._devPackagesInstall();
    }

    /**
     * END
     *
     * @returns {undefined}
     */
    end() {
        this.log('5. DONE - Awesome!');
    }

    /**
     * CREATE PROJECT FOLDER
     *
     * @returns {undefined}
     * @private
     */
    _createProjectFolder() {
        mkdirp(this.options.projectName);
        this.destinationRoot(this.destinationPath(this.options.projectName));
    }

    _copySharedFiles() {
        this._copyStaticFiles('shared/static');
    }

    /**
     * COPY BASE FILES
     *
     * @returns {undefined}
     * @private
     */
    _copyBaseFiles() {
        this._copyStaticFiles('base/static');

        // Dynamic Templates
        this.fs.copyTpl(
            this.templatePath('base/dynamic/package.json'),
            this.destinationPath('package.json'),
            {
                projectName: this.options.projectName
            }
        );
    }

    /**
     * COPY SPA FILES
     * Files only needed for Single Page Applications
     *
     * @returns {undefined}
     * @private
     */
    _copySpaFiles() {
        this._copyStaticFiles('spa/static');

        // Dynamic Templates
        this.fs.copyTpl(
            this.templatePath('spa/dynamic/package.json'),
            this.destinationPath('package.json'),
            {
                projectName: this.options.projectName
            }
        );

        this.fs.copyTpl(
            this.templatePath('spa/dynamic/manifest.json'),
            this.destinationPath('public/manifest.json'),
            {
                appName: this._appName,
                appNameShort: this._appNameShort
            }
        );
    }

    /**
     * COPY STATIC TEMPLATES
     *
     * @param {string} path - destination to copy templates
     * @returns {undefined}
     * @private
     */
    _copyStaticFiles(path) {
        fs.readdir(this.templatePath(path), (err, files) => {
            if (err) this.log(err);

            files.forEach((file) => {
                this.fs.copy(
                    this.templatePath(`${path}/${file}`),
                    this.destinationPath(file)
                );
            });
        });
    }

    /**
     * Install packages
     *
     * @returns {undefined}
     * @private
     */
    _packagesInstall() {
        const dependencies = [
            'react',
            'react-dom',
            'sass-mq'
        ];

        if (this.options.spa) {
            dependencies.push('cerebral@beta');
            dependencies.push('@cerebral/router');
        }

        if (this.options.yarn) {
            this.yarnInstall(dependencies);
        } else {
            this.npmInstall(dependencies, { 'save': true });
        }
    }

    /**
     * Install dev-packages
     *
     * @returns {undefined}
     * @private
     */
    _devPackagesInstall() {
        const devDependencies = [
            'eslint',
            'eslint-plugin-import',
            'eslint-plugin-promise',
            'eslint-plugin-standard',
            'eslint-plugin-react',
            'eslint-plugin-jsx-a11y',
            'eslint-config-emakinacee-base',
            'eslint-config-emakinacee-react',
            'react-scripts',
            'node-sass-chokidar',
            'npm-run-all',
            'serve'
        ];

        if (this.options.yarn) {
            this.yarnInstall(devDependencies, { 'dev': true });
        } else {
            this.npmInstall(devDependencies, { 'save-dev': true });
        }
    }
};

module.exports = AppGenerator;
<|endoftext|>


<commit_before>/*
 * (c) Copyright Ascensio System SIA 2010-2018
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";
/**
 * User: Ilja.Kirillov
 * Date: 11.10.2017
 * Time: 13:47
 */

/**
 *
 * @constructor
 * @extends {CParagraphContentBase}
 */
function CParagraphBookmark(isStart, sBookmarkId, sBookmarkName)
{
	CParagraphContentBase.call(this);
	this.Id = AscCommon.g_oIdCounter.Get_NewId();

	this.Type         = para_Bookmark;
	this.Start        = isStart ? true : false;
	this.BookmarkId   = sBookmarkId;
	this.BookmarkName = sBookmarkName;
	this.Use          = true;
	this.PageAbs      = 1;
	this.X            = 0;
	this.Y            = 0;

	AscCommon.g_oTableId.Add(this, this.Id);
}

CParagraphBookmark.prototype = Object.create(CParagraphContentBase.prototype);
CParagraphBookmark.prototype.constructor = CParagraphBookmark;

CParagraphBookmark.prototype.Get_Id = function()
{
	return this.Id;
};
CParagraphBookmark.prototype.GetId = function()
{
	return this.Id;
};
CParagraphBookmark.prototype.Copy = function()
{
	return new CParagraphBookmark(this.Start, this.BookmarkId, this.BookmarkName);
};
CParagraphBookmark.prototype.GetBookmarkId = function()
{
	return this.BookmarkId;
};
CParagraphBookmark.prototype.GetBookmarkName = function()
{
	return this.BookmarkName;
};
CParagraphBookmark.prototype.IsUse = function()
{
	return this.Use;
};
CParagraphBookmark.prototype.SetUse = function(isUse)
{
	this.Use = isUse;
};
CParagraphBookmark.prototype.UpdateBookmarks = function(oManager)
{
	oManager.ProcessBookmarkChar(this);
};
CParagraphBookmark.prototype.IsStart = function()
{
	return this.Start;
};
CParagraphBookmark.prototype.Recalculate_Range = function(PRS, ParaPr)
{
	this.PageAbs = PRS.Paragraph.Get_AbsolutePage(PRS.Page);

	this.X = PRS.X;
	this.Y = PRS.Y;
};
CParagraphBookmark.prototype.GetPage = function()
{
	return this.PageAbs;
};
CParagraphBookmark.prototype.GetXY = function()
{
	return {X : this.X, Y : this.Y};
};
CParagraphBookmark.prototype.GoToBookmark = function()
{
	var oParagraph = this.Paragraph;
	if (!oParagraph)
		return;

	var oLogicDocument = oParagraph.LogicDocument;
	if (!oLogicDocument)
		return;

	var oCurPos = oParagraph.Get_PosByElement(this);
	if (!oCurPos)
		return;

	oLogicDocument.RemoveSelection();
	oParagraph.Set_ParaContentPos(oCurPos, false, -1, -1, true); // Корректировать позицию нужно обязательно
	oParagraph.Document_SetThisElementCurrent(true);
};
CParagraphBookmark.prototype.RemoveBookmark = function()
{
	var oParagraph = this.Paragraph;
	if (!oParagraph)
		return;

	var oCurPos = oParagraph.Get_PosByElement(this);
	if (!oCurPos)
		return;

	var oParent      = this.GetParent();
	var nPosInParent = this.GetPosInParent(oParent);

	if (!oParent || -1 === nPosInParent)
		return;

	oParent.Remove_FromContent(nPosInParent, 1);
};
//----------------------------------------------------------------------------------------------------------------------
// Функции совместного редактирования
//----------------------------------------------------------------------------------------------------------------------
CParagraphBookmark.prototype.Refresh_RecalcData = function()
{
};
CParagraphBookmark.prototype.Write_ToBinary2 = function(Writer)
{
	Writer.WriteLong(AscDFH.historyitem_type_ParaBookmark);

	// String   : Id
	// String   : Id закладки
	// String   : Имя закладки
	// Bool     : Start

	Writer.WriteString2("" + this.Id);
	Writer.WriteString2("" + this.BookmarkId);
	Writer.WriteString2(this.BookmarkName);
	Writer.WriteBool(this.Start);
};
CParagraphBookmark.prototype.Read_FromBinary2 = function(Reader)
{
	this.Id           = Reader.GetString2();
	this.BookmarkId   = Reader.GetString2();
	this.BookmarkName = Reader.GetString2();
	this.Start        = Reader.GetBool();
};

function CBookmarksManager(oLogicDocument)
{
	this.LogicDocument = oLogicDocument;

	// Список всех закладок
	this.Bookmarks = [];

	// Массив с временными элементами
	this.BookmarksChars = {};

	// Нужно ли обновлять список закладок
	this.NeedUpdate = true;

	this.IdCounter    = 0;
	this.IdCounterTOC = 0;
}
CBookmarksManager.prototype.SetNeedUpdate = function(isNeed)
{
	this.NeedUpdate = isNeed;
};
CBookmarksManager.prototype.IsNeedUpdate = function()
{
	return this.NeedUpdate;
};
CBookmarksManager.prototype.BeginCollectingProcess = function()
{
	this.Bookmarks      = [];
	this.BookmarksChars = {};

	this.IdCounter    = 0;
	this.IdCounterTOC = 0;
};
CBookmarksManager.prototype.ProcessBookmarkChar = function(oParaBookmark)
{
	if (!(oParaBookmark instanceof CParagraphBookmark))
		return;

	var sBookmarkId = oParaBookmark.GetBookmarkId();
	if (undefined !== this.BookmarksChars[sBookmarkId])
	{
		if (oParaBookmark.IsStart())
		{
			oParaBookmark.SetUse(false);
		}
		else
		{
			this.BookmarksChars[sBookmarkId].SetUse(true);
			oParaBookmark.SetUse(true);
			this.Bookmarks.push([this.BookmarksChars[sBookmarkId], oParaBookmark]);
			delete this.BookmarksChars[sBookmarkId];
		}
	}
	else
	{
		if (!oParaBookmark.IsStart())
			oParaBookmark.SetUse(false);
		else
			this.BookmarksChars[sBookmarkId] = oParaBookmark;
	}

	if (oParaBookmark.IsStart())
	{
		var sBookmarkName = oParaBookmark.GetBookmarkName();
		if (sBookmarkName && 0 === sBookmarkName.indexOf("_Toc"))
		{
			var nId = parseInt(sBookmarkName.substring(4));
			if (!isNaN(nId))
				this.IdCounterTOC = Math.max(this.IdCounterTOC, nId);
		}

		var nId = parseInt(sBookmarkId);
		if (!isNaN(nId))
			this.IdCounter = Math.max(this.IdCounter, nId);
	}
};
CBookmarksManager.prototype.EndCollectingProcess = function()
{
	for (var sId in this.BookmarksChars)
	{
		this.BookmarksChars[sId].SetUse(false);
	}

	this.BookmarksChars = {};

	this.NeedUpdate = false;
};
CBookmarksManager.prototype.GetBookmarkById = function(Id)
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		if (this.Bookmarks[nIndex].GetBookmarkId() === Id)
			return this.Bookmarks[nIndex];
	}

	return null;
};
CBookmarksManager.prototype.GetBookmarkByName = function(sName)
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		var oStart = this.Bookmarks[nIndex][0];
		if (oStart.GetBookmarkName() === sName)
			return this.Bookmarks[nIndex];
	}

	return null;
};
CBookmarksManager.prototype.HaveBookmark = function(sName)
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		var oStart = this.Bookmarks[nIndex][0];
		if (oStart.GetBookmarkName() === sName)
			return true;
	}

	return false;
};
CBookmarksManager.prototype.Update = function()
{
	if (this.NeedUpdate)
		this.LogicDocument.UpdateBookmarks();
};
CBookmarksManager.prototype.GetNewBookmarkId = function()
{
	this.Update();

	return ("" + ++this.IdCounter);
};
CBookmarksManager.prototype.GetNewBookmarkNameTOC = function()
{
	this.Update();

	return ("_Toc" + ++this.IdCounterTOC);
};
CBookmarksManager.prototype.RemoveTOCBookmarks = function()
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		var oStart = this.Bookmarks[nIndex][0];
		var oEnd   = this.Bookmarks[nIndex][1];

		if (0 === oStart.GetBookmarkName().toLowerCase().indexOf("_toc"))
		{
			oStart.RemoveBookmark();
			oEnd.RemoveBookmark();
		}
	}
};
CBookmarksManager.prototype.GetCount = function()
{
	this.Update();

	return this.Bookmarks.length;
};
CBookmarksManager.prototype.GetName = function(nIndex)
{
	this.Update();

	if (nIndex < 0 || this.Index >= this.Bookmarks.length)
		return "";

	return this.Bookmarks[nIndex][0].GetBookmarkName();
};
CBookmarksManager.prototype.GetId = function(nIndex)
{
	this.Update();

	if (nIndex < 0 || this.Index >= this.Bookmarks.length)
		return "";

	return this.Bookmarks[nIndex][0].GetBookmarkId();
};
CBookmarksManager.prototype.RemoveBookmark = function(sName)
{
	this.Update();

	if (!this.GetBookmarkByName(sName))
		return;

	this.LogicDocument.RemoveBookmark(sName);
};
CBookmarksManager.prototype.AddBookmark = function(sName)
{
	this.Update();

	if (this.GetBookmarkByName(sName))
		return;

	this.LogicDocument.AddBookmark(sName);
};
CBookmarksManager.prototype.GoToBookmark = function(sName)
{
	this.Update();

	var oBookmark = this.GetBookmarkByName(sName);
	if (oBookmark)
		oBookmark[0].GoToBookmark();
};
CBookmarksManager.prototype.IsHiddenBookmark = function(sName)
{
	return (sName && '_' === sName.charAt(0));
};
CBookmarksManager.prototype.IsInternalUseBookmark = function(sName)
{
	return (sName === "_GoBack");
};
CBookmarksManager.prototype.CheckNewBookmarkName = function(sName)
{
	if (!sName)
		return false;

	return (sName === XRegExp.match(sName, new XRegExp('(\\pL)(\\pL|\_|\\pN){0,39}')));
};
CBookmarksManager.prototype.GetNameForHeadingBookmark = function(oParagraph)
{
	if (!oParagraph)
		return "";

	var sText = oParagraph.GetText();

	var nPos = 0;
	while (nPos < sText.length)
	{
		var nChar = sText.charCodeAt(nPos);
		if (0x0020 !== nChar && 0x0009 !== nChar)
			break;
	}

	var sName = "";
	for (var nIndex = nPos, nLen = Math.min(sText.length, nPos + 10); nIndex < nLen; ++nIndex)
	{
		var nChar = sText.charCodeAt(nIndex);
		if (0x0020 === nChar || 0x0009 === nChar)
			sName += "_";
		else
			sName += sText.charAt(nIndex);
	}

	if (!sName)
		return "";

	return "_" + sName;
};

//--------------------------------------------------------export----------------------------------------------------
window['AscCommonWord'] = window['AscCommonWord'] || {};
window['AscCommonWord'].CParagraphBookmark = CParagraphBookmark;
CBookmarksManager.prototype['asc_GetCount']              = CBookmarksManager.prototype.GetCount;
CBookmarksManager.prototype['asc_GetName']               = CBookmarksManager.prototype.GetName;
CBookmarksManager.prototype['asc_GetId']                 = CBookmarksManager.prototype.GetId;
CBookmarksManager.prototype['asc_AddBookmark']           = CBookmarksManager.prototype.AddBookmark;
CBookmarksManager.prototype['asc_RemoveBookmark']        = CBookmarksManager.prototype.RemoveBookmark;
CBookmarksManager.prototype['asc_GoToBookmark']          = CBookmarksManager.prototype.GoToBookmark;
CBookmarksManager.prototype['asc_HaveBookmark']          = CBookmarksManager.prototype.HaveBookmark;
CBookmarksManager.prototype['asc_IsHiddenBookmark']      = CBookmarksManager.prototype.IsHiddenBookmark;
CBookmarksManager.prototype['asc_IsInternalUseBookmark'] = CBookmarksManager.prototype.IsInternalUseBookmark;
CBookmarksManager.prototype['asc_CheckNewBookmarkName']  = CBookmarksManager.prototype.CheckNewBookmarkName;

<commit_msg>Fix bug #38782<commit_after>/*
 * (c) Copyright Ascensio System SIA 2010-2018
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";
/**
 * User: Ilja.Kirillov
 * Date: 11.10.2017
 * Time: 13:47
 */

/**
 *
 * @constructor
 * @extends {CParagraphContentBase}
 */
function CParagraphBookmark(isStart, sBookmarkId, sBookmarkName)
{
	CParagraphContentBase.call(this);
	this.Id = AscCommon.g_oIdCounter.Get_NewId();

	this.Type         = para_Bookmark;
	this.Start        = isStart ? true : false;
	this.BookmarkId   = sBookmarkId;
	this.BookmarkName = sBookmarkName;
	this.Use          = true;
	this.PageAbs      = 1;
	this.X            = 0;
	this.Y            = 0;

	AscCommon.g_oTableId.Add(this, this.Id);
}

CParagraphBookmark.prototype = Object.create(CParagraphContentBase.prototype);
CParagraphBookmark.prototype.constructor = CParagraphBookmark;

CParagraphBookmark.prototype.Get_Id = function()
{
	return this.Id;
};
CParagraphBookmark.prototype.GetId = function()
{
	return this.Id;
};
CParagraphBookmark.prototype.Copy = function()
{
	return new CParagraphBookmark(this.Start, this.BookmarkId, this.BookmarkName);
};
CParagraphBookmark.prototype.GetBookmarkId = function()
{
	return this.BookmarkId;
};
CParagraphBookmark.prototype.GetBookmarkName = function()
{
	return this.BookmarkName;
};
CParagraphBookmark.prototype.IsUse = function()
{
	return this.Use;
};
CParagraphBookmark.prototype.SetUse = function(isUse)
{
	this.Use = isUse;
};
CParagraphBookmark.prototype.UpdateBookmarks = function(oManager)
{
	oManager.ProcessBookmarkChar(this);
};
CParagraphBookmark.prototype.IsStart = function()
{
	return this.Start;
};
CParagraphBookmark.prototype.Recalculate_Range = function(PRS, ParaPr)
{
	this.PageAbs = PRS.Paragraph.Get_AbsolutePage(PRS.Page);

	this.X = PRS.X;
	this.Y = PRS.Y;
};
CParagraphBookmark.prototype.GetPage = function()
{
	return this.PageAbs;
};
CParagraphBookmark.prototype.GetXY = function()
{
	return {X : this.X, Y : this.Y};
};
CParagraphBookmark.prototype.GoToBookmark = function()
{
	var oParagraph = this.Paragraph;
	if (!oParagraph)
		return;

	var oLogicDocument = oParagraph.LogicDocument;
	if (!oLogicDocument)
		return;

	var oCurPos = oParagraph.Get_PosByElement(this);
	if (!oCurPos)
		return;

	oLogicDocument.RemoveSelection();
	oParagraph.Set_ParaContentPos(oCurPos, false, -1, -1, true); // Корректировать позицию нужно обязательно
	oParagraph.Document_SetThisElementCurrent(true);
};
CParagraphBookmark.prototype.RemoveBookmark = function()
{
	var oParagraph = this.Paragraph;
	if (!oParagraph)
		return;

	var oCurPos = oParagraph.Get_PosByElement(this);
	if (!oCurPos)
		return;

	var oParent      = this.GetParent();
	var nPosInParent = this.GetPosInParent(oParent);

	if (!oParent || -1 === nPosInParent)
		return;

	oParent.Remove_FromContent(nPosInParent, 1);
};
//----------------------------------------------------------------------------------------------------------------------
// Функции совместного редактирования
//----------------------------------------------------------------------------------------------------------------------
CParagraphBookmark.prototype.Refresh_RecalcData = function()
{
};
CParagraphBookmark.prototype.Write_ToBinary2 = function(Writer)
{
	Writer.WriteLong(AscDFH.historyitem_type_ParaBookmark);

	// String   : Id
	// String   : Id закладки
	// String   : Имя закладки
	// Bool     : Start

	Writer.WriteString2("" + this.Id);
	Writer.WriteString2("" + this.BookmarkId);
	Writer.WriteString2(this.BookmarkName);
	Writer.WriteBool(this.Start);
};
CParagraphBookmark.prototype.Read_FromBinary2 = function(Reader)
{
	this.Id           = Reader.GetString2();
	this.BookmarkId   = Reader.GetString2();
	this.BookmarkName = Reader.GetString2();
	this.Start        = Reader.GetBool();
};

function CBookmarksManager(oLogicDocument)
{
	this.LogicDocument = oLogicDocument;

	// Список всех закладок
	this.Bookmarks = [];

	// Массив с временными элементами
	this.BookmarksChars = {};

	// Нужно ли обновлять список закладок
	this.NeedUpdate = true;

	this.IdCounter    = 0;
	this.IdCounterTOC = 0;
}
CBookmarksManager.prototype.SetNeedUpdate = function(isNeed)
{
	this.NeedUpdate = isNeed;
};
CBookmarksManager.prototype.IsNeedUpdate = function()
{
	return this.NeedUpdate;
};
CBookmarksManager.prototype.BeginCollectingProcess = function()
{
	this.Bookmarks      = [];
	this.BookmarksChars = {};

	this.IdCounter    = 0;
	this.IdCounterTOC = 0;
};
CBookmarksManager.prototype.ProcessBookmarkChar = function(oParaBookmark)
{
	if (!(oParaBookmark instanceof CParagraphBookmark))
		return;

	var sBookmarkId = oParaBookmark.GetBookmarkId();
	if (undefined !== this.BookmarksChars[sBookmarkId])
	{
		if (oParaBookmark.IsStart())
		{
			oParaBookmark.SetUse(false);
		}
		else
		{
			this.BookmarksChars[sBookmarkId].SetUse(true);
			oParaBookmark.SetUse(true);
			this.Bookmarks.push([this.BookmarksChars[sBookmarkId], oParaBookmark]);
			delete this.BookmarksChars[sBookmarkId];
		}
	}
	else
	{
		if (!oParaBookmark.IsStart())
			oParaBookmark.SetUse(false);
		else
			this.BookmarksChars[sBookmarkId] = oParaBookmark;
	}

	if (oParaBookmark.IsStart())
	{
		var sBookmarkName = oParaBookmark.GetBookmarkName();
		if (sBookmarkName && 0 === sBookmarkName.indexOf("_Toc"))
		{
			var nId = parseInt(sBookmarkName.substring(4));
			if (!isNaN(nId))
				this.IdCounterTOC = Math.max(this.IdCounterTOC, nId);
		}

		var nId = parseInt(sBookmarkId);
		if (!isNaN(nId))
			this.IdCounter = Math.max(this.IdCounter, nId);
	}
};
CBookmarksManager.prototype.EndCollectingProcess = function()
{
	for (var sId in this.BookmarksChars)
	{
		this.BookmarksChars[sId].SetUse(false);
	}

	this.BookmarksChars = {};

	this.NeedUpdate = false;
};
CBookmarksManager.prototype.GetBookmarkById = function(Id)
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		if (this.Bookmarks[nIndex].GetBookmarkId() === Id)
			return this.Bookmarks[nIndex];
	}

	return null;
};
CBookmarksManager.prototype.GetBookmarkByName = function(sName)
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		var oStart = this.Bookmarks[nIndex][0];
		if (oStart.GetBookmarkName() === sName)
			return this.Bookmarks[nIndex];
	}

	return null;
};
CBookmarksManager.prototype.HaveBookmark = function(sName)
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		var oStart = this.Bookmarks[nIndex][0];
		if (oStart.GetBookmarkName() === sName)
			return true;
	}

	return false;
};
CBookmarksManager.prototype.Update = function()
{
	if (this.NeedUpdate)
		this.LogicDocument.UpdateBookmarks();
};
CBookmarksManager.prototype.GetNewBookmarkId = function()
{
	this.Update();

	return ("" + ++this.IdCounter);
};
CBookmarksManager.prototype.GetNewBookmarkNameTOC = function()
{
	this.Update();

	return ("_Toc" + ++this.IdCounterTOC);
};
CBookmarksManager.prototype.RemoveTOCBookmarks = function()
{
	this.Update();

	for (var nIndex = 0, nCount = this.Bookmarks.length; nIndex < nCount; ++nIndex)
	{
		var oStart = this.Bookmarks[nIndex][0];
		var oEnd   = this.Bookmarks[nIndex][1];

		if (0 === oStart.GetBookmarkName().toLowerCase().indexOf("_toc"))
		{
			oStart.RemoveBookmark();
			oEnd.RemoveBookmark();
		}
	}
};
CBookmarksManager.prototype.GetCount = function()
{
	this.Update();

	return this.Bookmarks.length;
};
CBookmarksManager.prototype.GetName = function(nIndex)
{
	this.Update();

	if (nIndex < 0 || this.Index >= this.Bookmarks.length)
		return "";

	return this.Bookmarks[nIndex][0].GetBookmarkName();
};
CBookmarksManager.prototype.GetId = function(nIndex)
{
	this.Update();

	if (nIndex < 0 || this.Index >= this.Bookmarks.length)
		return "";

	return this.Bookmarks[nIndex][0].GetBookmarkId();
};
CBookmarksManager.prototype.RemoveBookmark = function(sName)
{
	this.Update();

	if (!this.GetBookmarkByName(sName))
		return;

	this.LogicDocument.RemoveBookmark(sName);
};
CBookmarksManager.prototype.AddBookmark = function(sName)
{
	this.Update();

	if (this.GetBookmarkByName(sName))
		return;

	this.LogicDocument.AddBookmark(sName);
};
CBookmarksManager.prototype.GoToBookmark = function(sName)
{
	this.Update();

	var oBookmark = this.GetBookmarkByName(sName);
	if (oBookmark)
		oBookmark[0].GoToBookmark();
};
CBookmarksManager.prototype.IsHiddenBookmark = function(sName)
{
	return (sName && '_' === sName.charAt(0));
};
CBookmarksManager.prototype.IsInternalUseBookmark = function(sName)
{
	return (sName === "_GoBack");
};
CBookmarksManager.prototype.CheckNewBookmarkName = function(sName)
{
	if (!sName)
		return false;

	return (sName === XRegExp.match(sName, new XRegExp('(\\pL)(\\pL|\_|\\pN){0,39}')));
};
CBookmarksManager.prototype.GetNameForHeadingBookmark = function(oParagraph)
{
	if (!oParagraph)
		return "";

	var sText = oParagraph.GetText();

	var nStartPos = 0;
	while (nStartPos < sText.length)
	{
		var nChar = sText.charCodeAt(nStartPos);
		if (0x0020 !== nChar && 0x0009 !== nChar)
			break;

		nStartPos++;
	}

	var sName = "";
	for (var nIndex = nStartPos, nLen = Math.min(sText.length, nStartPos + 10); nIndex < nLen; ++nIndex)
	{
		var nChar = sText.charCodeAt(nIndex);
		if (0x0020 === nChar || 0x0009 === nChar)
			sName += "_";
		else
			sName += sText.charAt(nIndex);
	}

	if (!sName)
		return "";

	return "_" + sName;
};

//--------------------------------------------------------export----------------------------------------------------
window['AscCommonWord'] = window['AscCommonWord'] || {};
window['AscCommonWord'].CParagraphBookmark = CParagraphBookmark;
CBookmarksManager.prototype['asc_GetCount']              = CBookmarksManager.prototype.GetCount;
CBookmarksManager.prototype['asc_GetName']               = CBookmarksManager.prototype.GetName;
CBookmarksManager.prototype['asc_GetId']                 = CBookmarksManager.prototype.GetId;
CBookmarksManager.prototype['asc_AddBookmark']           = CBookmarksManager.prototype.AddBookmark;
CBookmarksManager.prototype['asc_RemoveBookmark']        = CBookmarksManager.prototype.RemoveBookmark;
CBookmarksManager.prototype['asc_GoToBookmark']          = CBookmarksManager.prototype.GoToBookmark;
CBookmarksManager.prototype['asc_HaveBookmark']          = CBookmarksManager.prototype.HaveBookmark;
CBookmarksManager.prototype['asc_IsHiddenBookmark']      = CBookmarksManager.prototype.IsHiddenBookmark;
CBookmarksManager.prototype['asc_IsInternalUseBookmark'] = CBookmarksManager.prototype.IsInternalUseBookmark;
CBookmarksManager.prototype['asc_CheckNewBookmarkName']  = CBookmarksManager.prototype.CheckNewBookmarkName;

<|endoftext|>


<commit_before>Ember.RadioButton = Ember.View.extend({
    tagName : "input",
    type : "radio",
    attributeBindings : [ "name", "type", "value", "checked:checked:" ],
    click : function() {
        this.set("selection", this.$().val());
    },
    checked : function() {
       return this.get("value") == this.get("selection");
    }.property()
});

Boundless = Ember.Namespace.create({
  VERSION: '0.1.0'
});

Boundless.MapLayer = Ember.Object.extend({
    title: null,
    visible: null,
    group: null,
    exclusive: false,
    init: function(layer) {
        this.title = layer.get('title');
        this.visible = layer.get('visible');
        this.group = layer.get('group');
        this.exclusive = layer.get('exclusive');
        this.addObserver('selected', layer, function(evt) {
            this.set('visible', (evt.get('selected') === 'on'));
        });
        this.addObserver('visible', layer, function(evt) {
            this.set('visible', evt.get('visible'));
        });
        layer.on('change:title', function(evt) {
            this.set('title', evt.target.get('title'));
        }, this);
        layer.on('change:visible', function(evt) {
            this.set('visible', evt.target.get('visible'));
        }, this);
        delete this.layer;
    }
});

Boundless.LayerSwitcherComponent = Ember.Component.extend({
  init: function() {
      this._super();
      this.addObserver('selected', function(evt) {
          var group = evt.get('group');
          var title = evt.get('selected');
          this.model.forEach(function(item) {
              if (item.get('group') === group) {
                  item.set('visible', (item.get('title') === title));
              }
          });
      });
      var lc = map.getLayers();
      // if an item is removed from the layers collection, update our view
      lc.on('remove', function(evt) {
          var el = evt.getElement();
          this.model.removeObject(this.model.findBy('title', el.get('title')));
      }, this);
  },
  actions: {
    removeSelected: function(item) {
        var idx = this.model.indexOf(item);
        if (idx !== -1) {
            map.getLayers().removeAt(idx);
        }
        this.model.removeObject(item);
    }
  }
});

Boundless.layersToModel = function(layers) {
    var model = [];
    for (var i =0, ii=layers.length; i<ii; ++i) {
        model.push(new Boundless.MapLayer(layers[i]));
    }
    return model;
};
<commit_msg>remove unused code<commit_after>Ember.RadioButton = Ember.View.extend({
    tagName : "input",
    type : "radio",
    attributeBindings : [ "name", "type", "value", "checked:checked:" ],
    click : function() {
        this.set("selection", this.$().val());
    },
    checked : function() {
       return this.get("value") == this.get("selection");
    }.property()
});

Boundless = Ember.Namespace.create({
  VERSION: '0.1.0'
});

Boundless.MapLayer = Ember.Object.extend({
    title: null,
    visible: null,
    group: null,
    exclusive: false,
    init: function(layer) {
        this.title = layer.get('title');
        this.visible = layer.get('visible');
        this.group = layer.get('group');
        this.exclusive = layer.get('exclusive');
        this.addObserver('visible', layer, function(evt) {
            this.set('visible', evt.get('visible'));
        });
        layer.on('change:title', function(evt) {
            this.set('title', evt.target.get('title'));
        }, this);
        layer.on('change:visible', function(evt) {
            this.set('visible', evt.target.get('visible'));
        }, this);
        delete this.layer;
    }
});

Boundless.LayerSwitcherComponent = Ember.Component.extend({
  init: function() {
      this._super();
      this.addObserver('selected', function(evt) {
          var group = evt.get('group');
          var title = evt.get('selected');
          this.model.forEach(function(item) {
              if (item.get('group') === group) {
                  item.set('visible', (item.get('title') === title));
              }
          });
      });
      var lc = map.getLayers();
      // if an item is removed from the layers collection, update our view
      lc.on('remove', function(evt) {
          var el = evt.getElement();
          this.model.removeObject(this.model.findBy('title', el.get('title')));
      }, this);
  },
  actions: {
    removeSelected: function(item) {
        var idx = this.model.indexOf(item);
        if (idx !== -1) {
            map.getLayers().removeAt(idx);
        }
        this.model.removeObject(item);
    }
  }
});

Boundless.layersToModel = function(layers) {
    var model = [];
    for (var i =0, ii=layers.length; i<ii; ++i) {
        model.push(new Boundless.MapLayer(layers[i]));
    }
    return model;
};
<|endoftext|>