<commit_before>/**
 * The UI Element for browsing and selecting pre-defined Pedigree templates
 *
 * @class TemplateSelector
 * @constructor
 * @param {Boolean} isStartupTemplateSelector Set to True if no pedigree has been loaded yet
 */
var TemplateSelector = Class.create({

	initialize: function (isStartupTemplateSelector) {
		this._isStartupTemplateSelector = isStartupTemplateSelector;
		this.mainDiv = new Element('div', {'class': 'template-picture-container'});
		this.mainDiv.update("Loading list of templates...");
		var closeShortcut = isStartupTemplateSelector ? [] : ['Esc'];
		this.dialog = new PhenoTips.widgets.ModalPopup(this.mainDiv, {close: {method: this.hide.bind(this), keys: closeShortcut}}, {extraClassName: "pedigree-template-chooser", title: "Please select a pedigree template", displayCloseButton: !isStartupTemplateSelector, verticalPosition: "top"});
		isStartupTemplateSelector && this.show();
		//Commented by Soheil for GEL(GenomicsEngland)
		//ge the right path instead of using xWiki path
		//new Ajax.Request(new XWiki.Document('WebHome').getRestURL('objects/PhenoTips.PedigreeClass/index.xml').substring(1), {
		var settings = new Settings();

		//Commented by Soheil for GEL(GenomicsEngland)
		//ge the right path instead of using xWiki path
		//new Ajax.Request(new XWiki.Document('WebHome').getRestURL('objects/PhenoTips.PedigreeClass/index.xml').substring(1), {
		var settings = new Settings();
		debugger
		var newURL = settings.getAbsoluteURL("/" + new XWiki.Document('WebHome').getRestURL('objects/PhenoTips.PedigreeClass/index.xml').substring(1));
		new Ajax.Request(newURL , {
			method: 'GET',
			onSuccess: this._onTemplateListAvailable.bind(this)
		});
	},

	/**
	 * Returns True if this template selector is the one displayed on startup
	 *
	 * @method isStartupTemplateSelector
	 * @return {Boolean}
	 */
	isStartupTemplateSelector: function () {
		return this._isStartupTemplateSelector;
	},

	/**
	 * Displays the templates once they have been downloaded
	 *
	 * @param response
	 * @private
	 */
	_onTemplateListAvailable: function (response) {
		this.mainDiv.update();
		var objects = response.responseXML.documentElement.getElementsByTagName('objectSummary');
		for (var i = 0; i < objects.length; ++i) {
			var pictureBox = new Element('div', {'class': 'picture-box'});
			pictureBox.update("Loading...");
			this.mainDiv.insert(pictureBox);
			var href = getSelectorFromXML(objects[i], "link", "rel", "http://www.xwiki.org/rel/properties").getAttribute("href");
			// Use only the path, since the REST module returns the wrong host behind a reverse proxy
			var path = href.substring(href.indexOf("/", href.indexOf("//") + 2));
			var settings = new Settings();
			debugger
			var newURL = settings.getAbsoluteURL("/rest" + path);
			//new Ajax.Request(href, {
			new Ajax.Request(newURL, {
				method: 'GET',
				onSuccess: this._onTemplateAvailable.bind(this, pictureBox)
			});
		}
	},

	/**
	 * Creates a clickable template thumbnail once the information has been downloaded
	 *
	 * @param pictureBox
	 * @param response
	 * @private
	 */
	_onTemplateAvailable: function (pictureBox, response) {
		pictureBox.innerHTML = getSubSelectorTextFromXML(response.responseXML, "property", "name", "image", "value").replace(/&/, '&');
		pictureBox.pedigreeData = getSubSelectorTextFromXML(response.responseXML, "property", "name", "data", "value");
		pictureBox.type = 'internal';
		pictureBox.description = getSubSelectorTextFromXML(response.responseXML, "property", "name", "description", "value");
		pictureBox.title = pictureBox.description;

		//console.log("[Data from Template] - " + Helpers.stringifyObject(pictureBox.pedigreeData));

		// TODO: render images with JavaScript instead
		if (window.SVGSVGElement &&
			document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1")) {
			pictureBox.update(getSubSelectorTextFromXML(response.responseXML, "property", "name", "image", "value"));
		} else {
			pictureBox.innerHTML = "

 " + pictureBox.description + "

";
		}
		pictureBox.observe('click', this._onTemplateSelected.bindAsEventListener(this, pictureBox));
	},

	/**
	 * Loads the template once it has been selected
	 *
	 * @param event
	 * @param pictureBox
	 * @private
	 */
	_onTemplateSelected: function (event, pictureBox) {
		//console.log("observe onTemplateSelected");
		this.dialog.close();
		if (pictureBox.type == 'internal') {
			editor.getSaveLoadEngine().createGraphFromSerializedData(pictureBox.pedigreeData, false /* add to undo stack */, true /*center around 0*/);
		} else if (pictureBox.type == 'simpleJSON') {
			editor.getSaveLoadEngine().createGraphFromImportData(pictureBox.pedigreeData, 'simpleJSON', {}, false /* add to undo stack */, true /*center around 0*/);
		}
	},

	/**
	 * Displays the template selector
	 *
	 * @method show
	 */
	show: function () {
		var availableHeight = document.viewport.getHeight() - 80;
		this.mainDiv.setStyle({'max-height': availableHeight + 'px', 'overflow-y': 'auto'});
		this.dialog.show();
	},

	/**
	 * Removes the the template selector
	 *
	 * @method hide
	 */
	hide: function () {
		this.dialog.closeDialog();
	}
});

// TODO: replace XML loading with a service providing JSONs

function unescapeRestData(data) {
	// http://stackoverflow.com/questions/4480757/how-do-i-unescape-html-entities-in-js-change-lt-to
	var tempNode = document.createElement('div');
	tempNode.innerHTML = data.replace(/&/, '&');
	return tempNode.innerText || tempNode.text || tempNode.textContent;
}

function getSelectorFromXML(responseXML, selectorName, attributeName, attributeValue) {
	if (responseXML.querySelector) {
		// modern browsers
		return responseXML.querySelector(selectorName + "[" + attributeName + "='" + attributeValue + "']");
	} else {
		// IE7 && IE8 && some other older browsers
		// http://www.w3schools.com/XPath/xpath_syntax.asp
		// http://msdn.microsoft.com/en-us/library/ms757846%28v=vs.85%29.aspx
		var query = ".//" + selectorName + "[@" + attributeName + "='" + attributeValue + "']";
		try {
			return responseXML.selectSingleNode(query);
		} catch (e) {
			// Firefox v3.0-
			alert("your browser is unsupported");
			window.stop && window.stop();
			throw "Unsupported browser";
		}
	}
}

function getSubSelectorTextFromXML(responseXML, selectorName, attributeName, attributeValue, subselectorName) {
	var selector = getSelectorFromXML(responseXML, selectorName, attributeName, attributeValue);

	var value = selector.innerText || selector.text || selector.textContent;

	if (!value)     // fix IE behavior where (undefined || "" || undefined) == undefined
		value = "";

	return value;
}<commit_msg>Remove debugger<commit_after>/**
 * The UI Element for browsing and selecting pre-defined Pedigree templates
 *
 * @class TemplateSelector
 * @constructor
 * @param {Boolean} isStartupTemplateSelector Set to True if no pedigree has been loaded yet
 */
var TemplateSelector = Class.create({

	initialize: function (isStartupTemplateSelector) {
		this._isStartupTemplateSelector = isStartupTemplateSelector;
		this.mainDiv = new Element('div', {'class': 'template-picture-container'});
		this.mainDiv.update("Loading list of templates...");
		var closeShortcut = isStartupTemplateSelector ? [] : ['Esc'];
		this.dialog = new PhenoTips.widgets.ModalPopup(this.mainDiv, {close: {method: this.hide.bind(this), keys: closeShortcut}}, {extraClassName: "pedigree-template-chooser", title: "Please select a pedigree template", displayCloseButton: !isStartupTemplateSelector, verticalPosition: "top"});
		isStartupTemplateSelector && this.show();
		//Commented by Soheil for GEL(GenomicsEngland)
		//ge the right path instead of using xWiki path
		//new Ajax.Request(new XWiki.Document('WebHome').getRestURL('objects/PhenoTips.PedigreeClass/index.xml').substring(1), {
		var settings = new Settings();

		//Commented by Soheil for GEL(GenomicsEngland)
		//ge the right path instead of using xWiki path
		//new Ajax.Request(new XWiki.Document('WebHome').getRestURL('objects/PhenoTips.PedigreeClass/index.xml').substring(1), {
		var settings = new Settings();
		var newURL = settings.getAbsoluteURL("/" + new XWiki.Document('WebHome').getRestURL('objects/PhenoTips.PedigreeClass/index.xml').substring(1));
		new Ajax.Request(newURL , {
			method: 'GET',
			onSuccess: this._onTemplateListAvailable.bind(this)
		});
	},

	/**
	 * Returns True if this template selector is the one displayed on startup
	 *
	 * @method isStartupTemplateSelector
	 * @return {Boolean}
	 */
	isStartupTemplateSelector: function () {
		return this._isStartupTemplateSelector;
	},

	/**
	 * Displays the templates once they have been downloaded
	 *
	 * @param response
	 * @private
	 */
	_onTemplateListAvailable: function (response) {
		this.mainDiv.update();
		var objects = response.responseXML.documentElement.getElementsByTagName('objectSummary');
		for (var i = 0; i < objects.length; ++i) {
			var pictureBox = new Element('div', {'class': 'picture-box'});
			pictureBox.update("Loading...");
			this.mainDiv.insert(pictureBox);
			var href = getSelectorFromXML(objects[i], "link", "rel", "http://www.xwiki.org/rel/properties").getAttribute("href");
			// Use only the path, since the REST module returns the wrong host behind a reverse proxy
			var path = href.substring(href.indexOf("/", href.indexOf("//") + 2));
			var settings = new Settings();
			var newURL = settings.getAbsoluteURL("/rest" + path);
			//new Ajax.Request(href, {
			new Ajax.Request(newURL, {
				method: 'GET',
				onSuccess: this._onTemplateAvailable.bind(this, pictureBox)
			});
		}
	},

	/**
	 * Creates a clickable template thumbnail once the information has been downloaded
	 *
	 * @param pictureBox
	 * @param response
	 * @private
	 */
	_onTemplateAvailable: function (pictureBox, response) {
		pictureBox.innerHTML = getSubSelectorTextFromXML(response.responseXML, "property", "name", "image", "value").replace(/&/, '&');
		pictureBox.pedigreeData = getSubSelectorTextFromXML(response.responseXML, "property", "name", "data", "value");
		pictureBox.type = 'internal';
		pictureBox.description = getSubSelectorTextFromXML(response.responseXML, "property", "name", "description", "value");
		pictureBox.title = pictureBox.description;

		//console.log("[Data from Template] - " + Helpers.stringifyObject(pictureBox.pedigreeData));

		// TODO: render images with JavaScript instead
		if (window.SVGSVGElement &&
			document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1")) {
			pictureBox.update(getSubSelectorTextFromXML(response.responseXML, "property", "name", "image", "value"));
		} else {
			pictureBox.innerHTML = "

 " + pictureBox.description + "

";
		}
		pictureBox.observe('click', this._onTemplateSelected.bindAsEventListener(this, pictureBox));
	},

	/**
	 * Loads the template once it has been selected
	 *
	 * @param event
	 * @param pictureBox
	 * @private
	 */
	_onTemplateSelected: function (event, pictureBox) {
		//console.log("observe onTemplateSelected");
		this.dialog.close();
		if (pictureBox.type == 'internal') {
			editor.getSaveLoadEngine().createGraphFromSerializedData(pictureBox.pedigreeData, false /* add to undo stack */, true /*center around 0*/);
		} else if (pictureBox.type == 'simpleJSON') {
			editor.getSaveLoadEngine().createGraphFromImportData(pictureBox.pedigreeData, 'simpleJSON', {}, false /* add to undo stack */, true /*center around 0*/);
		}
	},

	/**
	 * Displays the template selector
	 *
	 * @method show
	 */
	show: function () {
		var availableHeight = document.viewport.getHeight() - 80;
		this.mainDiv.setStyle({'max-height': availableHeight + 'px', 'overflow-y': 'auto'});
		this.dialog.show();
	},

	/**
	 * Removes the the template selector
	 *
	 * @method hide
	 */
	hide: function () {
		this.dialog.closeDialog();
	}
});

// TODO: replace XML loading with a service providing JSONs

function unescapeRestData(data) {
	// http://stackoverflow.com/questions/4480757/how-do-i-unescape-html-entities-in-js-change-lt-to
	var tempNode = document.createElement('div');
	tempNode.innerHTML = data.replace(/&/, '&');
	return tempNode.innerText || tempNode.text || tempNode.textContent;
}

function getSelectorFromXML(responseXML, selectorName, attributeName, attributeValue) {
	if (responseXML.querySelector) {
		// modern browsers
		return responseXML.querySelector(selectorName + "[" + attributeName + "='" + attributeValue + "']");
	} else {
		// IE7 && IE8 && some other older browsers
		// http://www.w3schools.com/XPath/xpath_syntax.asp
		// http://msdn.microsoft.com/en-us/library/ms757846%28v=vs.85%29.aspx
		var query = ".//" + selectorName + "[@" + attributeName + "='" + attributeValue + "']";
		try {
			return responseXML.selectSingleNode(query);
		} catch (e) {
			// Firefox v3.0-
			alert("your browser is unsupported");
			window.stop && window.stop();
			throw "Unsupported browser";
		}
	}
}

function getSubSelectorTextFromXML(responseXML, selectorName, attributeName, attributeValue, subselectorName) {
	var selector = getSelectorFromXML(responseXML, selectorName, attributeName, attributeValue);

	var value = selector.innerText || selector.text || selector.textContent;

	if (!value)     // fix IE behavior where (undefined || "" || undefined) == undefined
		value = "";

	return value;
}<|endoftext|>


<commit_before>
// From http://www.tutorialspoint.com/javascript/array_map.htm
if (!Array.prototype.map)
{
  Array.prototype.map = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun !== "function")
      throw new TypeError();

    var res = new Array(len);
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        res[i] = fun.call(thisp, this[i], i, this);
    }

    return res;
  };
}

L.Control.FuseSearch = L.Control.extend({

    includes: L.Mixin.Events,

    options: {
        position: 'topright',
        panelWidth: 360,
        title: 'Search',
        placeholder: 'Search',
        caseSensitive: false,
        threshold: 0.5,
        maxResultLength: null,
        showResultFct: null,
        showInvisibleFeatures: true
    },

    initialize: function(options) {
        L.setOptions(this, options);
        this._panelOnLeftSide = (this.options.position.indexOf("left") !== -1);
    },

    onAdd: function(map) {

        var ctrl = this._createControl();
        //this._createPanel(map);
        this._setEventListeners();
        map.invalidateSize();

        return ctrl;
    },

    onRemove: function(map) {

        this.hidePanel(map);
        this._clearEventListeners();
        //this._clearPanel(map);
        this._clearControl();

        return this;
    },

    _createControl: function() {

        var _this = this;
        var container = this._panel = L.DomUtil.create('div', 'leaflet-fusesearch-control');

        // Control to open the search panel
        var butt = this._openButton = L.DomUtil.create('a', 'button', container);
        butt.href = '#';
        //butt.innerHTML = '&#f002';
        butt.title = this.options.title;
        var stop = L.DomEvent.stopPropagation;
        L.DomEvent.on(butt, 'click', stop)
                  .on(butt, 'mousedown', stop)
                  .on(butt, 'touchstart', stop)
                  .on(butt, 'mousewheel', stop)
                  .on(butt, 'MozMousePixelScroll', stop);
        L.DomEvent.on(butt, 'click', L.DomEvent.preventDefault);
        L.DomEvent.on(butt, 'click', this.showPanel, this);

        // place the panel on the same side as the control
        if (this._panelOnLeftSide) {
            L.DomUtil.addClass(container, 'left');
        } else {
            L.DomUtil.addClass(container, 'right');
        }

        //return container;

        //L.DomUtil.create('img', 'search-image', container);
        this._input = L.DomUtil.create('input', 'search-input', container);
        this._input.maxLength = 30;
        this._input.placeholder = this.options.placeholder;
        this._input.onkeyup = function(evt) {
            var searchString = evt.currentTarget.value;
            _this.searchFeatures(searchString);
        };

        // Close button
        var close = this._closeButton = L.DomUtil.create('a', 'close', container);
        close.innerHTML = '×';
        L.DomEvent.on(close, 'click', this.hidePanel, this);

        // Where the result will be listed
        this._resultList = L.DomUtil.create('div', 'result-list', container);

        //return pane;
        return container;
    },

    _clearControl: function() {
        // Unregister events to prevent memory leak
        var butt = this._openButton;
        var stop = L.DomEvent.stopPropagation;
        L.DomEvent.off(butt, 'click', stop)
                  .off(butt, 'mousedown', stop)
                  .off(butt, 'touchstart', stop)
                  .off(butt, 'mousewheel', stop)
                  .off(butt, 'MozMousePixelScroll', stop);
        L.DomEvent.off(butt, 'click', L.DomEvent.preventDefault);
        L.DomEvent.off(butt, 'click', this.showPanel);

        L.DomEvent.off(this._closeButton, 'click', this.hidePanel);
    },

    /*_createPanel: function(map) {
        var _this = this;

        // Create the search panel
        var mapContainer = map.getContainer();
        var className = 'leaflet-fusesearch-panel',
            pane = this._panel = L.DomUtil.create('div', className, mapContainer);

        // Make sure we don't drag the map when we interact with the content
        var stop = L.DomEvent.stopPropagation;
        L.DomEvent.on(pane, 'click', stop)
                .on(pane, 'dblclick', stop)
                .on(pane, 'mousedown', stop)
                .on(pane, 'touchstart', stop)
                .on(pane, 'mousewheel', stop)
                .on(pane, 'MozMousePixelScroll', stop);

        // place the pane on the same side as the control
        if (this._panelOnLeftSide) {
            L.DomUtil.addClass(pane, 'left');
        } else {
            L.DomUtil.addClass(pane, 'right');
        }

        // Intermediate container to get the box sizing right
        var container = L.DomUtil.create('div', 'content', pane);

        // Search image and input field
        L.DomUtil.create('img', 'search-image', container);
        this._input = L.DomUtil.create('input', 'search-input', container);
        this._input.maxLength = 30;
        this._input.placeholder = this.options.placeholder;
        this._input.onkeyup = function(evt) {
            var searchString = evt.currentTarget.value;
            _this.searchFeatures(searchString);
        };

        // Close button
        var close = this._closeButton = L.DomUtil.create('a', 'close', container);
        close.innerHTML = '×';
        L.DomEvent.on(close, 'click', this.hidePanel, this);

        // Where the result will be listed
        this._resultList = L.DomUtil.create('div', 'result-list', container);

        return pane;
    },*/

    /*_clearPanel: function(map) {

        // Unregister event handlers
        var stop = L.DomEvent.stopPropagation;
        L.DomEvent.off(this._panel, 'click', stop)
                  .off(this._panel, 'dblclick', stop)
                  .off(this._panel, 'mousedown', stop)
                  .off(this._panel, 'touchstart', stop)
                  .off(this._panel, 'mousewheel', stop)
                  .off(this._panel, 'MozMousePixelScroll', stop);

        L.DomEvent.off(this._closeButton, 'click', this.hidePanel);

        var mapContainer = map.getContainer();
        mapContainer.removeChild(this._panel);

        this._panel = null;
    },*/

    _setEventListeners : function() {
        var that = this;
        var input = this._input;
        this._map.addEventListener('overlayadd', function() {
            that.searchFeatures(input.value);
        });
        this._map.addEventListener('overlayremove', function() {
            that.searchFeatures(input.value);
        });
    },

    _clearEventListeners: function() {
        this._map.removeEventListener('overlayadd');
        this._map.removeEventListener('overlayremove');
    },

    isPanelVisible: function () {
        return L.DomUtil.hasClass(this._panel, 'visible');
    },

    showPanel: function () {
        if (! this.isPanelVisible()) {
            L.DomUtil.addClass(this._panel, 'visible');
            L.DomEvent.off(this._openButton, 'click', this.hidePanel);
            // Preserve map centre
            this._map.panBy([this.getOffset() * 0.5, 0], {duration: 0.5});
            this.fire('show');
            L.DomUtil.addClass(this._input, 'visible');
            L.DomUtil.addClass(this._closeButton, 'visible');
            L.DomUtil.addClass(this._resultList, 'visible');
            this._input.select();
            // Search again as visibility of features might have changed
            this.searchFeatures(this._input.value);
        }
    },

    hidePanel: function (e) {
        if (this.isPanelVisible()) {
            L.DomUtil.removeClass(this._panel, 'visible');
            L.DomUtil.removeClass(this._input, 'visible');
            L.DomUtil.removeClass(this._closeButton, 'visible');
            L.DomUtil.removeClass(this._resultList, 'visible');
            // Move back the map centre - only if we still hold this._map
            // as this might already have been cleared up by removeFrom()
            if (null !== this._map) {
                this._map.panBy([this.getOffset() * -0.5, 0], {duration: 0.5});
            };
            this.fire('hide');
            if(e) {
                L.DomEvent.stopPropagation(e);
            }
        }
    },

    getOffset: function() {
        console.log(this._panel.offsetWidth);
        if (this._panelOnLeftSide) {
            return - this.options.panelWidth;
        } else {
            return this.options.panelWidth;
        }
    },

    indexFeatures: function(jsonFeatures, keys) {

        this._keys = keys;
        var properties = jsonFeatures.map(function(feature) {
            // Keep track of the original feature
            feature.properties._feature = feature;
            return feature.properties;
        });

        var options = {
            keys: keys,
            caseSensitive: this.options.caseSensitive,
            threshold : this.options.threshold
        };

        this._fuseIndex = new Fuse(properties, options);
    },

    searchFeatures: function(string) {

        var result = this._fuseIndex.search(string);

        // Empty result list
        $(".result-item").remove();

        var resultList = $('.result-list')[0];
        var num = 0;
        var max = this.options.maxResultLength;
        for (var i in result) {
            var props = result[i];
            var feature = props._feature;
            //var popup = this._getFeaturePopupIfVisible(feature);

            if (/*undefined !== popup || */!this.options.showInvisibleFeatures) {
                this.createResultItem(props, resultList);//, popup);
                if (undefined !== max && ++num === max)
                    break;
            }
        }
    },

    _getFeaturePopupIfVisible: function(feature) {
        var layer = feature.layer;
        if (undefined !== layer && this._map.hasLayer(layer)) {
            //return layer.getPopup();
            console.log(layer);
            //return layer.openPopup();
        }
    },

    createResultItem: function(props, container) {//, popup) {

        var _this = this;
        var feature = props._feature;

        // Create a container and open the associated popup on click
        var resultItem = L.DomUtil.create('p', 'result-item', container);

        //if (undefined !== popup) {
            L.DomUtil.addClass(resultItem, 'clickable');
            resultItem.onclick = function() {
                _this.hidePanel();
                feature.layer.openPopup();
                map.fitBounds(feature.layer.getBounds(), {'duration': 0.95});
            };
        //}

        // Fill in the container with the user-supplied function if any,
        // otherwise display the feature properties used for the search.
        if (null !== this.options.showResultFct) {
            this.options.showResultFct(feature, resultItem);
        } else {
            str = '' + props[this._keys[0]] + '';
            for (var i = 1; i < this._keys.length; i++) {
                str += '
' + props[this._keys[i]];
            }
            resultItem.innerHTML = str;
        };

        return resultItem;
    }//,
 /*
    _panAndPopup : function(feature, popup) {
        // Temporarily adapt the map padding so that the popup is not hidden by the search pane
        if (this._panelOnLeftSide) {
            var oldPadding = popup.options.autoPanPaddingTopLeft;
            var newPadding = new L.Point(- this.getOffset(), 10);
            popup.options.autoPanPaddingTopLeft = newPadding;
            feature.layer.openPopup();
            popup.options.autoPanPaddingTopLeft = oldPadding;
        } else {
            var oldPadding = popup.options.autoPanPaddingBottomRight;
            var newPadding = new L.Point(this.getOffset(), 10);
            popup.options.autoPanPaddingBottomRight = newPadding;
            feature.layer.openPopup();
            popup.options.autoPanPaddingBottomRight = oldPadding;
        }
    }
 */
});

L.control.fuseSearch = function(options) {
    return new L.Control.FuseSearch(options);
};<commit_msg>Redundant code from fusesearch.js removed<commit_after>// From http://www.tutorialspoint.com/javascript/array_map.htm
if (!Array.prototype.map)
{
  Array.prototype.map = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun !== "function")
      throw new TypeError();

    var res = new Array(len);
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        res[i] = fun.call(thisp, this[i], i, this);
    }

    return res;
  };
}

L.Control.FuseSearch = L.Control.extend({

    includes: L.Mixin.Events,

    options: {
        position: 'topright',
        panelWidth: 360,
        title: 'Search',
        placeholder: 'Search',
        caseSensitive: false,
        threshold: 0.5,
        maxResultLength: null,
        showResultFct: null,
        showInvisibleFeatures: true
    },

    initialize: function(options) {
        L.setOptions(this, options);
        this._panelOnLeftSide = (this.options.position.indexOf("left") !== -1);
    },

    onAdd: function(map) {

        var ctrl = this._createControl();
        this._setEventListeners();
        map.invalidateSize();

        return ctrl;
    },

    onRemove: function(map) {

        this.hidePanel(map);
        this._clearEventListeners();
        this._clearControl();

        return this;
    },

    _createControl: function() {

        var _this = this;
        var container = this._panel = L.DomUtil.create('div', 'leaflet-fusesearch-control');

        // Control to open the search panel
        var butt = this._openButton = L.DomUtil.create('a', 'button', container);
        butt.href = '#';
        butt.title = this.options.title;
        var stop = L.DomEvent.stopPropagation;
        L.DomEvent.on(butt, 'click', stop)
                  .on(butt, 'mousedown', stop)
                  .on(butt, 'touchstart', stop)
                  .on(butt, 'mousewheel', stop)
                  .on(butt, 'MozMousePixelScroll', stop);
        L.DomEvent.on(butt, 'click', L.DomEvent.preventDefault);
        L.DomEvent.on(butt, 'click', this.showPanel, this);

        // place the panel on the same side as the control
        if (this._panelOnLeftSide) {
            L.DomUtil.addClass(container, 'left');
        } else {
            L.DomUtil.addClass(container, 'right');
        }

        this._input = L.DomUtil.create('input', 'search-input', container);
        this._input.maxLength = 30;
        this._input.placeholder = this.options.placeholder;
        this._input.onkeyup = function(evt) {
            var searchString = evt.currentTarget.value;
            _this.searchFeatures(searchString);
        };

        // Close button
        var close = this._closeButton = L.DomUtil.create('a', 'close', container);
        close.innerHTML = '×';
        L.DomEvent.on(close, 'click', this.hidePanel, this);

        // Where the result will be listed
        this._resultList = L.DomUtil.create('div', 'result-list', container);

        return container;
    },

    _clearControl: function() {
        // Unregister events to prevent memory leak
        var butt = this._openButton;
        var stop = L.DomEvent.stopPropagation;
        L.DomEvent.off(butt, 'click', stop)
                  .off(butt, 'mousedown', stop)
                  .off(butt, 'touchstart', stop)
                  .off(butt, 'mousewheel', stop)
                  .off(butt, 'MozMousePixelScroll', stop);
        L.DomEvent.off(butt, 'click', L.DomEvent.preventDefault);
        L.DomEvent.off(butt, 'click', this.showPanel);

        L.DomEvent.off(this._closeButton, 'click', this.hidePanel);
    },

    _setEventListeners : function() {
        var that = this;
        var input = this._input;
        this._map.addEventListener('overlayadd', function() {
            that.searchFeatures(input.value);
        });
        this._map.addEventListener('overlayremove', function() {
            that.searchFeatures(input.value);
        });
    },

    _clearEventListeners: function() {
        this._map.removeEventListener('overlayadd');
        this._map.removeEventListener('overlayremove');
    },

    isPanelVisible: function () {
        return L.DomUtil.hasClass(this._panel, 'visible');
    },

    showPanel: function () {
        if (! this.isPanelVisible()) {
            L.DomUtil.addClass(this._panel, 'visible');
            L.DomEvent.off(this._openButton, 'click', this.hidePanel);
            // Preserve map centre
            this._map.panBy([this.getOffset() * 0.5, 0], {duration: 0.5});
            this.fire('show');
            L.DomUtil.addClass(this._input, 'visible');
            L.DomUtil.addClass(this._closeButton, 'visible');
            L.DomUtil.addClass(this._resultList, 'visible');
            this._input.select();
            // Search again as visibility of features might have changed
            this.searchFeatures(this._input.value);
        }
    },

    hidePanel: function (e) {
        if (this.isPanelVisible()) {
            L.DomUtil.removeClass(this._panel, 'visible');
            L.DomUtil.removeClass(this._input, 'visible');
            L.DomUtil.removeClass(this._closeButton, 'visible');
            L.DomUtil.removeClass(this._resultList, 'visible');
            // Move back the map centre - only if we still hold this._map
            // as this might already have been cleared up by removeFrom()
            if (null !== this._map) {
                this._map.panBy([this.getOffset() * -0.5, 0], {duration: 0.5});
            };
            this.fire('hide');
            if(e) {
                L.DomEvent.stopPropagation(e);
            }
        }
    },

    getOffset: function() {
        if (this._panelOnLeftSide) {
            return - this.options.panelWidth;
        } else {
            return this.options.panelWidth;
        }
    },

    indexFeatures: function(jsonFeatures, keys) {

        this._keys = keys;
        var properties = jsonFeatures.map(function(feature) {
            // Keep track of the original feature
            feature.properties._feature = feature;
            return feature.properties;
        });

        var options = {
            keys: keys,
            caseSensitive: this.options.caseSensitive,
            threshold : this.options.threshold
        };

        this._fuseIndex = new Fuse(properties, options);
    },

    searchFeatures: function(string) {

        var result = this._fuseIndex.search(string);

        // Empty result list
        $(".result-item").remove();

        var resultList = $('.result-list')[0];
        var num = 0;
        var max = this.options.maxResultLength;
        for (var i in result) {
            var props = result[i];
            var feature = props._feature;

            if (!this.options.showInvisibleFeatures) {
                this.createResultItem(props, resultList);
                if (undefined !== max && ++num === max)
                    break;
            }
        }
    },

    createResultItem: function(props, container) {

        var _this = this;
        var feature = props._feature;

        // Create a container and open the associated popup on click
        var resultItem = L.DomUtil.create('p', 'result-item', container);

        L.DomUtil.addClass(resultItem, 'clickable');
        resultItem.onclick = function() {
            _this.hidePanel();
            map.fitBounds(feature.layer.getBounds(), {'duration': 0.95});
            feature.layer.openPopup();
        };

        // Fill in the container with the user-supplied function if any,
        // otherwise display the feature properties used for the search.
        if (null !== this.options.showResultFct) {
            this.options.showResultFct(feature, resultItem);
        } else {
            str = '' + props[this._keys[0]] + '';
            for (var i = 1; i < this._keys.length; i++) {
                str += '
' + props[this._keys[i]];
            }
            resultItem.innerHTML = str;
        };

        return resultItem;
    }
});

L.control.fuseSearch = function(options) {
    return new L.Control.FuseSearch(options);
};<|endoftext|>


<commit_before>/* globals $, chrome */
var usedOn = {};
var openedOn = {};
var accessed = {};
var activeTabId;

function _debug() {
    // console.log.apply(console, arguments);
}

function _getMax() {
    return parseInt(localStorage.max || 20);
}

function _getAlgo() {
    return localStorage.algo || 'used';
}

function _handleTabActivated(data) {
    var tabId = data.tabId;
    activeTabId = tabId;
    _debug('activated', tabId);

    usedOn[tabId] = new Date().getTime();
    accessed[tabId] += 1;
}

function _handleTabRemoved(tabId) {
    _debug('removed', tabId);
    delete usedOn[tabId];
    delete openedOn[tabId];
    delete accessed[tabId];
}

function _removeTab(tabId) {
    _debug('_removeTab', tabId);
    if (tabId) {
        chrome.tabs.remove(tabId, function() {});
        _handleTabRemoved(tabId);
    }
}

function _getLowestIn(data, tabs) {
    var lowest;
    var lowestIndex;
    var tabId;
    var value;
    for (var i = 0; i < tabs.length; i++) {
        tabId = tabs[i].id;

        // never close the currently active tab
        if (tabId === activeTabId) {
            continue;
        }

        // if you have never been to this tab then skip it
        if (!data.hasOwnProperty(tabId)) {
            continue;
        }

        value = data[tabId] || 0;

        if (lowest === undefined) {
            lowest = value;
        }

        if (value <= lowest) {
            lowestIndex = i;
            lowest = value;
        }
    }

    return lowestIndex;
}

function _removeLeastAccessed(tabs) {
    var removeTabIndex = _getLowestIn(accessed, tabs);
    if (removeTabIndex >= 0) {
        _removeTab(tabs[removeTabIndex].id);
        tabs.splice(removeTabIndex, 1);
    }
    return tabs;
}

function _removeOldest(tabs) {
    var removeTabIndex = _getLowestIn(openedOn, tabs);
    if (removeTabIndex >= 0) {
        _removeTab(tabs[removeTabIndex].id);
        tabs.splice(removeTabIndex, 1);
    }
    return tabs;
}

function _removeLeastRecentlyUsed(tabs) {
    var removeTabIndex = _getLowestIn(usedOn, tabs);
    if (removeTabIndex >= 0) {
        _removeTab(tabs[removeTabIndex].id);
        tabs.splice(removeTabIndex, 1);
    }
    return tabs;
}

function _removeTabs(tabs) {
    var length = tabs.length;
    _debug('there are', tabs.length, 'tabs open');
    _debug('max is', _getMax());
    while (length >= _getMax()) {
        _debug('removing a tab with length', length);
        switch (_getAlgo()) {
            case 'oldest':
                tabs = _removeOldest(tabs);
                break;
            case 'accessed':
                tabs = _removeLeastAccessed(tabs);
                break;
            default:
                tabs = _removeLeastRecentlyUsed(tabs);
                break;
        }
        length -= 1;
    }
}

function _handleTabAdded(data) {
    var tabId = data.id || data;

    _debug('added', tabId);

    // find tab to remove
    chrome.tabs.query({currentWindow: true}, function(tabs) {
        tabs = tabs.filter(function(tab) {
            return !tab.pinned;
        });

        tabs = tabs.filter(function(tab) {
            return tab.id != tabId;
        });

        if (tabs.length >= parseInt(localStorage.max, 10)) {
            _removeTabs(tabs);
        }

        openedOn[tabId] = new Date().getTime();
        accessed[tabId] = 0;
    });
}

function _init() {
    chrome.tabs.onActivated.addListener(_handleTabActivated);
    chrome.tabs.onCreated.addListener(_handleTabAdded);
    chrome.tabs.onAttached.addListener(_handleTabAdded);
    chrome.tabs.onRemoved.addListener(_handleTabRemoved);
    chrome.tabs.onDetached.addListener(_handleTabRemoved);
}

$.ready(_init);
<commit_msg>Do not remove pages more than once<commit_after>/* globals $, chrome */
var usedOn = {};
var openedOn = {};
var accessed = {};
var activeTabId;

function _debug() {
    // console.log.apply(console, arguments);
}

function _getMax() {
    return parseInt(localStorage.max || 20);
}

function _getAlgo() {
    return localStorage.algo || 'used';
}

function _handleTabActivated(data) {
    var tabId = data.tabId;
    activeTabId = tabId;
    _debug('activated', tabId);

    usedOn[tabId] = new Date().getTime();
    accessed[tabId] += 1;
}

function _handleTabRemoved(tabId) {
    _debug('removed', tabId);
    delete usedOn[tabId];
    delete openedOn[tabId];
    delete accessed[tabId];
}

function _removeTab(tabId) {
    _debug('_removeTab', tabId);
    if (tabId) {
        chrome.tabs.remove(tabId, function() {});
        // _handleTabRemoved(tabId);
    }
}

function _getLowestIn(data, tabs) {
    var lowest;
    var lowestIndex;
    var tabId;
    var value;
    for (var i = 0; i < tabs.length; i++) {
        tabId = tabs[i].id;

        // never close the currently active tab
        if (tabId === activeTabId) {
            continue;
        }

        // if you have never been to this tab then skip it
        if (!data.hasOwnProperty(tabId)) {
            continue;
        }

        value = data[tabId] || 0;

        if (lowest === undefined) {
            lowest = value;
        }

        if (value <= lowest) {
            lowestIndex = i;
            lowest = value;
        }
    }

    return lowestIndex;
}

function _removeLeastAccessed(tabs) {
    var removeTabIndex = _getLowestIn(accessed, tabs);
    if (removeTabIndex >= 0) {
        _removeTab(tabs[removeTabIndex].id);
        tabs.splice(removeTabIndex, 1);
    }
    return tabs;
}

function _removeOldest(tabs) {
    var removeTabIndex = _getLowestIn(openedOn, tabs);
    if (removeTabIndex >= 0) {
        _removeTab(tabs[removeTabIndex].id);
        tabs.splice(removeTabIndex, 1);
    }
    return tabs;
}

function _removeLeastRecentlyUsed(tabs) {
    var removeTabIndex = _getLowestIn(usedOn, tabs);
    if (removeTabIndex >= 0) {
        _removeTab(tabs[removeTabIndex].id);
        tabs.splice(removeTabIndex, 1);
    }
    return tabs;
}

function _removeTabs(tabs) {
    var length = tabs.length;
    _debug('there are', tabs.length, 'tabs open');
    _debug('max is', _getMax());
    while (length >= _getMax()) {
        _debug('removing a tab with length', length);
        switch (_getAlgo()) {
            case 'oldest':
                tabs = _removeOldest(tabs);
                break;
            case 'accessed':
                tabs = _removeLeastAccessed(tabs);
                break;
            default:
                tabs = _removeLeastRecentlyUsed(tabs);
                break;
        }
        length -= 1;
    }
}

function _handleTabAdded(data) {
    var tabId = data.id || data;

    _debug('added', tabId);

    // find tab to remove
    chrome.tabs.query({currentWindow: true}, function(tabs) {
        tabs = tabs.filter(function(tab) {
            return !tab.pinned;
        });

        tabs = tabs.filter(function(tab) {
            return tab.id != tabId;
        });

        if (tabs.length >= parseInt(localStorage.max, 10)) {
            _removeTabs(tabs);
        }

        openedOn[tabId] = new Date().getTime();
        accessed[tabId] = 0;
    });
}

function _init() {
    chrome.tabs.onActivated.addListener(_handleTabActivated);
    chrome.tabs.onCreated.addListener(_handleTabAdded);
    chrome.tabs.onAttached.addListener(_handleTabAdded);
    chrome.tabs.onRemoved.addListener(_handleTabRemoved);
    chrome.tabs.onDetached.addListener(_handleTabRemoved);
}

$.ready(_init);
<|endoftext|>


<commit_before>(function (window, $, d3, undefined) {

'use strict';

var modules   = window.setNamespace('app.modules'),
    app       = window.use('app'),
    EdgeEvent = window.use('app.event.EdgeEvent'),
    _defaults;

/**
 * EdgeCRUD module
 *
 * Adds functionality to link nodes by hovering them on top of each other
 */
modules.CRMEdgeCRUD = app.createClass(modules.EdgeCRUD, {

	construct: function (options) {

		this.options = $.extend({}, _defaults, options);

		this.mode = 'LINK';
	},

	initialize: function () {

		$(this.kernel).on(EdgeEvent.MODECHANGE, this.handleModeChange.bind(this));
	},

	handleModeChange: function (event, mode) {

		console.log("new mode: " + mode);

		this.mode = mode;
	},

	/**
     * Calculates the edge type to use for these nodes
     * and with the selected mode
     */
    resolveEdgeType: function (source, target) {
    	var edgeType = this.mode,
    		modes = this.options.modes;

        console.log("resolving edge type");
        console.log(edgeType);

    	if (edgeType === modes.LINK) {
    		if (source.type == 'test' || target.type == 'test') {
    			edgeType = 'LOLZ';
    		}
    	}

        return edgeType;
    }
});

}(window, window.jQuery, window.d3));<commit_msg>removed crm stuff<commit_after><|endoftext|>


<commit_before>/*
 * vim: ts=4:sw=4:expandtab
 */
(function () {
    'use strict';

    window.Whisper = window.Whisper || {};

    var SocketView = Whisper.View.extend({
        className: 'status',
        initialize: function() {
            setInterval(this.updateStatus.bind(this), 5000);
        },
        updateStatus: function() {
            var className, message = '';
            if (typeof getSocketStatus === 'function') {
              switch(getSocketStatus()) {
                  case WebSocket.CONNECTING:
                      className = 'connecting';
                      break;
                  case WebSocket.OPEN:
                      className = 'open';
                      break;
                  case WebSocket.CLOSING:
                      className = 'closing';
                      break;
                  case WebSocket.CLOSED:
                      className = 'closed';
                      message = i18n('disconnected');
                      break;
              }
            if (!this.$el.hasClass(className)) {
                this.$el.attr('class', className);
                this.$el.text(message);
            }
          }
        }
    });

    Whisper.ConversationStack = Whisper.View.extend({
        className: 'conversation-stack',
        open: function(conversation) {
            var id = 'conversation-' + conversation.cid;
            if (id !== this.el.firstChild.id) {
                this.$el.first().find('video, audio').each(function() {
                    this.pause();
                });
                var $el = this.$('#'+id);
                if ($el === null || $el.length === 0) {
                    var view = new Whisper.ConversationView({
                        model: conversation,
                        appWindow: this.model.appWindow
                    });
                    $el = view.$el;
                }
                $el.prependTo(this.el);
                conversation.trigger('opened');
            }
        }
    });

    Whisper.FontSizeView = Whisper.View.extend({
        defaultSize: 14,
        maxSize: 30,
        minSize: 14,
        initialize: function() {
            this.currentSize = this.defaultSize;
            this.render();
        },
        events: { 'mousewheel': 'zoomText' },
        zoomText: function(e) {
            if (e.ctrlKey === true) {
                if (e.originalEvent.deltaY > 0) {
                    if (this.currentSize > this.minSize) {
                        this.currentSize--;
                        this.render();
                    }
                } else if (e.originalEvent.deltaY < 0) {
                    if (this.currentSize < this.maxSize) {
                        this.currentSize++;
                        this.render();
                    }
                }
            }
        },
        render: function() {
            this.$el.css('font-size', this.currentSize + 'px');
        }
    });

    Whisper.InboxView = Whisper.View.extend({
        templateName: 'two-column',
        className: 'inbox',
        applyTheme: function() {
            var theme = storage.get('theme-setting') || 'android';
            this.$el.removeClass('ios')
                    .removeClass('android')
                    .addClass(theme);
        },
        initialize: function (options) {
            this.render();
            this.applyTheme();
            new Whisper.FontSizeView({ el: this.el });
            this.conversation_stack = new Whisper.ConversationStack({
                el: this.$('.conversation-stack'),
                model: { appWindow: options.appWindow }
            });

            var inboxCollection = getInboxCollection();
            this.inboxListView = new Whisper.ConversationListView({
                el         : this.$('.inbox'),
                collection : inboxCollection
            }).render();

            this.inboxListView.listenTo(inboxCollection,
                    'add change:timestamp change:name change:number',
                    this.inboxListView.sort);

            this.searchView = new Whisper.ConversationSearchView({
                el    : this.$('.search-results'),
                input : this.$('input.search')
            });

            this.searchView.$el.hide();

            this.listenTo(this.searchView, 'hide', function() {
                this.searchView.$el.hide();
                this.inboxListView.$el.show();
            });
            this.listenTo(this.searchView, 'show', function() {
                this.searchView.$el.show();
                this.inboxListView.$el.hide();
            });
            this.listenTo(this.searchView, 'open',
                this.openConversation.bind(this, null));

            new SocketView().render().$el.appendTo(this.$('.socket-status'));

            extension.windows.onClosed(function() {
                this.inboxListView.stopListening();
            }.bind(this));

            if (extension.expired()) {
                var banner = new Whisper.ExpiredAlertBanner().render();
                banner.$el.prependTo(this.$el);
                this.$el.addClass('expired');
            }
        },
        render_attributes: {
            welcomeToSignal         : i18n('welcomeToSignal'),
            selectAContact          : i18n('selectAContact'),
            searchForPeopleOrGroups : i18n('searchForPeopleOrGroups'),
            submitDebugLog          : i18n('submitDebugLog'),
            settings                : i18n('settings'),
            restartSignal           : i18n('restartSignal'),
        },
        events: {
            'click': 'onClick',
            'click #header': 'focusHeader',
            'click .conversation': 'focusConversation',
            'click .global-menu .hamburger': 'toggleMenu',
            'click .show-debug-log': 'showDebugLog',
            'click .showSettings': 'showSettings',
            'select .gutter .conversation-list-item': 'openConversation',
            'input input.search': 'filterContacts',
            'click .restart-signal': 'reloadBackgroundPage',
            'show .lightbox': 'showLightbox'
        },
        focusConversation: function(e) {
            if (e && this.$(e.target).closest('.placeholder').length) {
                return;
            }

            this.$('#header, .gutter').addClass('inactive');
            this.$('.conversation-stack').removeClass('inactive');
        },
        focusHeader: function() {
            this.$('.conversation-stack').addClass('inactive');
            this.$('#header, .gutter').removeClass('inactive');
            this.$('.conversation:first .menu').trigger('close');
        },
        reloadBackgroundPage: function() {
            chrome.runtime.reload();
        },
        showSettings: function() {
            var view = new Whisper.SettingsView();
            view.$el.appendTo(this.el);
            view.$el.on('change-theme', this.applyTheme.bind(this));
        },
        filterContacts: function(e) {
            this.searchView.filterContacts(e);
            var input = this.$('input.search');
            if (input.val().length > 0) {
                input.addClass('active');
            } else {
                input.removeClass('active');
            }
        },
        openConversation: function(e, conversation) {
            this.searchView.hideHints();
            conversation = ConversationController.create(conversation);
            this.conversation_stack.open(conversation);
            this.focusConversation();
        },
        toggleMenu: function() {
            this.$('.global-menu .menu-list').toggle();
        },
        showDebugLog: function() {
            this.$('.debug-log').remove();
            new Whisper.DebugLogView().$el.appendTo(this.el);
        },
        showLightbox: function(e) {
            this.$el.append(e.target);
        },
        closeRecording: function(e) {
            if (e && this.$(e.target).closest('.capture-audio').length > 0 ) {
                return;
            }
            this.$('.conversation:first .recorder').trigger('close');
        },
        closeMenu: function(e) {
            if (e && this.$(e.target).parent('.global-menu').length > 0 ) {
                return;
            }

            this.$('.global-menu .menu-list').hide();
        },
        onClick: function(e) {
            this.closeMenu(e);
            this.closeRecording(e);
        }
    });

    Whisper.ExpiredAlertBanner = Whisper.View.extend({
        templateName: 'expired_alert',
        className: 'expiredAlert clearfix',
        render_attributes: function() {
            return {
                expiredWarning: i18n('expiredWarning'),
                upgrade: i18n('upgrade'),
            };
        }
    });

})();
<commit_msg>Font size should be adjustable<commit_after>/*
 * vim: ts=4:sw=4:expandtab
 */
(function () {
    'use strict';

    window.Whisper = window.Whisper || {};

    var SocketView = Whisper.View.extend({
        className: 'status',
        initialize: function() {
            setInterval(this.updateStatus.bind(this), 5000);
        },
        updateStatus: function() {
            var className, message = '';
            if (typeof getSocketStatus === 'function') {
              switch(getSocketStatus()) {
                  case WebSocket.CONNECTING:
                      className = 'connecting';
                      break;
                  case WebSocket.OPEN:
                      className = 'open';
                      break;
                  case WebSocket.CLOSING:
                      className = 'closing';
                      break;
                  case WebSocket.CLOSED:
                      className = 'closed';
                      message = i18n('disconnected');
                      break;
              }
            if (!this.$el.hasClass(className)) {
                this.$el.attr('class', className);
                this.$el.text(message);
            }
          }
        }
    });

    Whisper.ConversationStack = Whisper.View.extend({
        className: 'conversation-stack',
        open: function(conversation) {
            var id = 'conversation-' + conversation.cid;
            if (id !== this.el.firstChild.id) {
                this.$el.first().find('video, audio').each(function() {
                    this.pause();
                });
                var $el = this.$('#'+id);
                if ($el === null || $el.length === 0) {
                    var view = new Whisper.ConversationView({
                        model: conversation,
                        appWindow: this.model.appWindow
                    });
                    $el = view.$el;
                }
                $el.prependTo(this.el);
                conversation.trigger('opened');
            }
        }
    });

    Whisper.FontSizeView = Whisper.View.extend({
        defaultSize: 14,
        maxSize: 30,
        minSize: 14,
        initialize: function() {
            this.currentSize = this.defaultSize;
            this.render();
        },
        events: { 'keydown': 'zoomText' },
        zoomText: function(e) {
            if (!e.ctrlKey)
                return;
            var keyCode = e.which || e.keyCode;
            var maxSize = 22; // if bigger text goes outside send-message textarea
            var minSize = 14;
            if (keyCode === 189 || keyCode == 109) {
                if (this.currentSize > minSize) {
                    this.currentSize--;
                }
            } else if (keyCode === 187 || keyCode == 107) {
                if (this.currentSize < maxSize) {
                    this.currentSize++;
                }
            }
            this.render();
        },
        render: function() {
            this.$el.css('font-size', this.currentSize + 'px');
        }
    });

    Whisper.InboxView = Whisper.View.extend({
        templateName: 'two-column',
        className: 'inbox',
        applyTheme: function() {
            var theme = storage.get('theme-setting') || 'android';
            this.$el.removeClass('ios')
                    .removeClass('android')
                    .addClass(theme);
        },
        initialize: function (options) {
            this.render();
            this.applyTheme();
            this.$el.attr('tabindex', '1');
            new Whisper.FontSizeView({ el: this.$el });
            this.conversation_stack = new Whisper.ConversationStack({
                el: this.$('.conversation-stack'),
                model: { appWindow: options.appWindow }
            });

            var inboxCollection = getInboxCollection();
            this.inboxListView = new Whisper.ConversationListView({
                el         : this.$('.inbox'),
                collection : inboxCollection
            }).render();

            this.inboxListView.listenTo(inboxCollection,
                    'add change:timestamp change:name change:number',
                    this.inboxListView.sort);

            this.searchView = new Whisper.ConversationSearchView({
                el    : this.$('.search-results'),
                input : this.$('input.search')
            });

            this.searchView.$el.hide();

            this.listenTo(this.searchView, 'hide', function() {
                this.searchView.$el.hide();
                this.inboxListView.$el.show();
            });
            this.listenTo(this.searchView, 'show', function() {
                this.searchView.$el.show();
                this.inboxListView.$el.hide();
            });
            this.listenTo(this.searchView, 'open',
                this.openConversation.bind(this, null));

            new SocketView().render().$el.appendTo(this.$('.socket-status'));

            extension.windows.onClosed(function() {
                this.inboxListView.stopListening();
            }.bind(this));

            if (extension.expired()) {
                var banner = new Whisper.ExpiredAlertBanner().render();
                banner.$el.prependTo(this.$el);
                this.$el.addClass('expired');
            }
        },
        render_attributes: {
            welcomeToSignal         : i18n('welcomeToSignal'),
            selectAContact          : i18n('selectAContact'),
            searchForPeopleOrGroups : i18n('searchForPeopleOrGroups'),
            submitDebugLog          : i18n('submitDebugLog'),
            settings                : i18n('settings'),
            restartSignal           : i18n('restartSignal'),
        },
        events: {
            'click': 'onClick',
            'click #header': 'focusHeader',
            'click .conversation': 'focusConversation',
            'click .global-menu .hamburger': 'toggleMenu',
            'click .show-debug-log': 'showDebugLog',
            'click .showSettings': 'showSettings',
            'select .gutter .conversation-list-item': 'openConversation',
            'input input.search': 'filterContacts',
            'click .restart-signal': 'reloadBackgroundPage',
            'show .lightbox': 'showLightbox'
        },
        focusConversation: function(e) {
            if (e && this.$(e.target).closest('.placeholder').length) {
                return;
            }

            this.$('#header, .gutter').addClass('inactive');
            this.$('.conversation-stack').removeClass('inactive');
        },
        focusHeader: function() {
            this.$('.conversation-stack').addClass('inactive');
            this.$('#header, .gutter').removeClass('inactive');
            this.$('.conversation:first .menu').trigger('close');
        },
        reloadBackgroundPage: function() {
            chrome.runtime.reload();
        },
        showSettings: function() {
            var view = new Whisper.SettingsView();
            view.$el.appendTo(this.el);
            view.$el.on('change-theme', this.applyTheme.bind(this));
        },
        filterContacts: function(e) {
            this.searchView.filterContacts(e);
            var input = this.$('input.search');
            if (input.val().length > 0) {
                input.addClass('active');
            } else {
                input.removeClass('active');
            }
        },
        openConversation: function(e, conversation) {
            this.searchView.hideHints();
            conversation = ConversationController.create(conversation);
            this.conversation_stack.open(conversation);
            this.focusConversation();
        },
        toggleMenu: function() {
            this.$('.global-menu .menu-list').toggle();
        },
        showDebugLog: function() {
            this.$('.debug-log').remove();
            new Whisper.DebugLogView().$el.appendTo(this.el);
        },
        showLightbox: function(e) {
            this.$el.append(e.target);
        },
        closeRecording: function(e) {
            if (e && this.$(e.target).closest('.capture-audio').length > 0 ) {
                return;
            }
            this.$('.conversation:first .recorder').trigger('close');
        },
        closeMenu: function(e) {
            if (e && this.$(e.target).parent('.global-menu').length > 0 ) {
                return;
            }

            this.$('.global-menu .menu-list').hide();
        },
        onClick: function(e) {
            this.closeMenu(e);
            this.closeRecording(e);
        }
    });

    Whisper.ExpiredAlertBanner = Whisper.View.extend({
        templateName: 'expired_alert',
        className: 'expiredAlert clearfix',
        render_attributes: function() {
            return {
                expiredWarning: i18n('expiredWarning'),
                upgrade: i18n('upgrade'),
            };
        }
    });

})();
<|endoftext|>


<commit_before>/*
 * quick-posts-controls.js
 * https://github.com/savetheinternet/Tinyboard/blob/master/js/quick-posts-controls.js
 *
 * Released under the MIT license
 * Copyright (c) 2012 Michael Save <savetheinternet@tinyboard.org>
 *
 * Usage:
 *   $config['additional_javascript'][] = 'js/jquery.min.js';
 *   $config['additional_javascript'][] = 'js/quick-post-controls.js';
 *
 */

$(document).ready(function(){
	var open_form = function() {
		var thread = $(this).parent().parent().hasClass('op');
		var id = $(this).attr('name').match(/^delete_(\d+)$/)[1];
		var submitButton;

		if(this.checked) {
			var post_form = $('<form class="post-actions" method="post" style="margin:10px 0 0 0">' +
				'
' +
					(!thread ? '
' : '') +

					'<input type="hidden" name="delete_' + id + '">' +

					''+_("Password")+': ' +
					'<input id="password_' + id + '" type="password" name="password" size="11" maxlength="18">' +
					'<input title="'+_('Delete file only')+'" type="checkbox" name="file" id="delete_file_' + id + '">' +
						''+_('File')+'' +
					' <input type="submit" name="delete" value="'+_('Delete')+'">' +

					'
' +

					''+_('Reason')+': ' +
					'<input id="reason_' + id + '" type="text" name="reason" size="20" maxlength="100">' +
					' <input type="submit" name="report" value="'+_('Report')+'">' +
				'
' +
			'</form>');
			post_form
				.attr('action', $('form:first').attr('action'))
				.append($('input[name=board]:first').clone())
				.find('input:not([type="checkbox"]):not([type="submit"]):not([type="hidden"])').keypress(function(e) {
					if(e.which == 13) {
						e.preventDefault();
						if($(this).attr('name') == 'password')  {
							post_form.find('input[name=delete]').click();
						} else if($(this).attr('name') == 'reason')  {
							post_form.find('input[name=report]').click();
						}

						return false;
					}

					return true;
				});

			post_form.find('input[type="password"]').val(localStorage.password);

			if(thread) {
				post_form.prependTo($(this).parent().parent().find('div.body'));
			} else {
				post_form.appendTo($(this).parent().parent());
				//post_form.insertBefore($(this));
			}

			$(window).trigger('quick-post-controls', post_form);
		} else {
			var elm = $(this).parent().parent().find('form');

			if(elm.attr('class') == 'post-actions')
				elm.remove();
		}
	};

	var init_qpc = function() {
		$(this).change(open_form);
		if(this.checked)
			$(this).trigger('change');
	};

	$('div.post input[type=checkbox].delete').each(init_qpc);

	$(document).bind('new_post', function(e, post) {
		$(post).find('input[type=checkbox].delete').each(init_qpc);
	});
});

<commit_msg>small fix "an unknown error occured"<commit_after>/*
 * quick-posts-controls.js
 * https://github.com/savetheinternet/Tinyboard/blob/master/js/quick-posts-controls.js
 *
 * Released under the MIT license
 * Copyright (c) 2012 Michael Save <savetheinternet@tinyboard.org>
 *
 * Usage:
 *   $config['additional_javascript'][] = 'js/jquery.min.js';
 *   $config['additional_javascript'][] = 'js/quick-post-controls.js';
 *
 */

$(document).ready(function(){
	var open_form = function() {
		var thread = $(this).parent().parent().hasClass('op');
		var id = $(this).attr('name').match(/^delete_(\d+)$/)[1];
		var submitButton;

		if(this.checked) {
			var post_form = $('<form class="post-actions" method="post" style="margin:10px 0 0 0">' +
				'
' +
					(!thread ? '
' : '') +

					'<input type="hidden" name="delete_' + id + '">' +

					''+_("Password")+': ' +
					'<input id="password_' + id + '" type="password" name="password" size="11" maxlength="18">' +
					'<input title="'+_('Delete file only')+'" type="checkbox" name="file" id="delete_file_' + id + '">' +
						''+_('File')+'' +
					' <input type="submit" name="delete" value="'+_('Delete')+'">' +

					'
' +

					''+_('Reason')+': ' +
					'<input id="reason_' + id + '" type="text" name="reason" size="20" maxlength="100">' +
					' <input type="submit" name="report" value="'+_('Report')+'">' +
				'
' +
			'</form>');
			post_form
				.attr('action', $('form[name="post"]:first').attr('action'))
				.append($('input[name=board]:first').clone())
				.find('input:not([type="checkbox"]):not([type="submit"]):not([type="hidden"])').keypress(function(e) {
					if(e.which == 13) {
						e.preventDefault();
						if($(this).attr('name') == 'password')  {
							post_form.find('input[name=delete]').click();
						} else if($(this).attr('name') == 'reason')  {
							post_form.find('input[name=report]').click();
						}

						return false;
					}

					return true;
				});

			post_form.find('input[type="password"]').val(localStorage.password);

			if(thread) {
				post_form.prependTo($(this).parent().parent().find('div.body'));
			} else {
				post_form.appendTo($(this).parent().parent());
				//post_form.insertBefore($(this));
			}

			$(window).trigger('quick-post-controls', post_form);
		} else {
			var elm = $(this).parent().parent().find('form');

			if(elm.attr('class') == 'post-actions')
				elm.remove();
		}
	};

	var init_qpc = function() {
		$(this).change(open_form);
		if(this.checked)
			$(this).trigger('change');
	};

	$('div.post input[type=checkbox].delete').each(init_qpc);

	$(document).bind('new_post', function(e, post) {
		$(post).find('input[type=checkbox].delete').each(init_qpc);
	});
});

<|endoftext|>


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

/**
 * The global toolbar
 */
var Upgrade = Backbone.View.extend({
    tagName: "div",

    events: {
        'click .upgradeheader' : 'call'
    },

    template: function() {
        var template = $("#template-upgrade").html() || "";

        return _.template(template)();
    },

    initialize: function() {

    },

    render: function() {
        if (!Settings.UPGRADE)
            return this;

        var timeout = Saiku.session.upgradeTimeout;
        var localStorageUsed = false;
        var first = true;
        if (typeof localStorage !== "undefined" && localStorage) {
            if (localStorage.getItem("saiku.upgradeTimeout") !== null) {
                timeout = localStorage.getItem("saiku.upgradeTimeout");
            }
            localStorageUsed = true;
        }

        var current = (new Date()).getTime();
        console.log("timeout: " + (!timeout) + " localstorage: " + localStorageUsed + " : diff " + (current - timeout));
        if (!timeout || (current - timeout) > (10 * 60 * 1000)) {
            $(this.el).html(this.template());
            Saiku.session.upgradeTimeout = current;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem("saiku.upgradeTimeout", current);
            }
        }

        return this;
    },

    call: function(e) {
        $(".upgradeheader").slideUp("slow");
        setTimeout("showIt()",15000);
    }

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

/**
 * The global toolbar
 */
var Upgrade = Backbone.View.extend({
    tagName: "div",

    events: {
        'click .upgradeheader' : 'call'
    },

    template: function() {
        var template = $("#template-upgrade").html() || "";

        return _.template(template)();
    },

    initialize: function() {

    },

    render: function() {
        if (!Settings.UPGRADE)
            return this;

        var timeout = Saiku.session.upgradeTimeout;
        var localStorageUsed = false;
        var first = true;
        if (typeof localStorage !== "undefined" && localStorage) {
            if (localStorage.getItem("saiku.upgradeTimeout") !== null) {
                timeout = localStorage.getItem("saiku.upgradeTimeout");
            }
            localStorageUsed = true;
        }

        var current = (new Date()).getTime();
        if (!timeout || (current - timeout) > (10 * 60 * 1000)) {
            $(this.el).html(this.template());
            Saiku.session.upgradeTimeout = current;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem("saiku.upgradeTimeout", current);
            }
        }

        return this;
    },

    call: function(e) {
        $(".upgradeheader").slideUp("slow");
        setTimeout("showIt()",15000);
    }

});
<|endoftext|>


<commit_before>

/**
 * HYP : i < j
 */

var hoare = function ( predicate, a, i, j ) {

	var x, t, o;

	o = i;
	x = a[o];

	while ( true ) {

		while ( true ) {

			--j;

			if ( i >= j ) {
				t    = a[o];
				a[o] = a[j];
				a[j] = t;
				return j;
			}

			if ( predicate( a[j], x ) ) {
				break;
			}
		}

		while ( true ) {

			++i;

			if ( i >= j ) {
				t    = a[o];
				a[o] = a[j];
				a[j] = t;
				return j;
			}

			if ( predicate( x, a[i] ) ) {
				break;
			}
		}

		// invariant i < j

		t    = a[i];
		a[i] = a[j];
		a[j] = t;

	}

};

exports.hoare = hoare;
exports.partition = hoare;
<commit_msg>fix #3<commit_after>

/**
 * HYP : i < j
 */

var hoare = function ( predicate, a, i, j ) {

	var x, t, o;

	o = i;
	x = a[o];

	while ( true ) {

		while ( true ) {

			--j;

			if ( i >= j ) {
				t    = a[o];
				a[o] = a[j];
				a[j] = t;
				return j;
			}

			else if ( predicate( a[j], x ) ) {
				break;
			}
		}

		while ( true ) {

			++i;

			if ( i >= j ) {
				t    = a[o];
				a[o] = a[j];
				a[j] = t;
				return j;
			}

			else if ( predicate( x, a[i] ) ) {
				break;
			}
		}

		// invariant i < j

		t    = a[i];
		a[i] = a[j];
		a[j] = t;

	}

};

exports.hoare = hoare;
exports.partition = hoare;
<|endoftext|>


<commit_before>function Text(msg) {
    this.id = msg;
    this.message = GetText(msg);
    this.read = false;
    this.active = false;
}
function Smartphone() {
    let texts = [ new Text("beckettText0"), new Text("beckettText1"), new Text("beckettText2"), new Text("beckettText3") ];
    let notifications = texts.length;
    let remainingTexts = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], pushedLimit = false;
    let datingApp = 0, datingAppMax = 7;
    let havePushedSister = false, sisterChance = 0;
    let lastX = 0, lastY = 0;
    this.GetPhoneData = function() {
        return {
            rt: remainingTexts, t: texts,
            hps: havePushedSister, sC: sisterChance,
            dA: datingApp, pL: pushedLimit
        };
    };
    this.SetPhoneData = function(t) {
        remainingTexts = t.rt;
        texts = t.t;
        notifications = 0;
        for(let i = 0; i < texts.length; i++) { if(!texts[i].read) { notifications++; } }
        havePushedSister = t.hps;
        sisterChance = t.sC;
        datingApp = t.dA;
        pushedLimit = t.pL;
    };
    this.Update = function() {
        if(worldmap.mapName !== "northcity") { return; }
        if(Math.round(worldmap.pos.x) % 4 !== 0 && Math.round(worldmap.pos.y) % 3 !== 0) { return; }
        if(Math.random() > 0.16) { return; }
        if(worldmap.pos.x === lastX || worldmap.pos.y === lastY) { return; }
        lastX = worldmap.pos.x; lastY = worldmap.pos.y;
        worldmap.smartphone.PushRandomText();
    };
    this.PushRandomText = function() {
        if(!havePushedSister) {
            if(Math.random() >= sisterChance) {
                sisterChance += 0.05;
            } else {
                havePushedSister = true;
                worldmap.smartphone.PushText("sistext0");
                setTimeout(function() { worldmap.smartphone.PushText("sistext1"); }, 1055);
                setTimeout(function() { worldmap.smartphone.PushText("sistext2"); }, 2203);
                setTimeout(function() { worldmap.smartphone.PushText("sistext3"); }, 3527);
                return;
            }
        }
        if(datingApp <= datingAppMax && Math.random() > 0.75) {
            worldmap.smartphone.PushText("sext" + (datingApp++));
        } else {
            const result = remainingTexts.splice(Math.floor(Math.random() * remainingTexts.length), 1)[0];
            if(result !== undefined) { worldmap.smartphone.PushText("misctext" + result); }
            else if(!pushedLimit && datingApp > datingAppMax) { worldmap.smartphone.PushText("misctext14"); pushedLimit = true; }
        }
    };
    this.PushText = function(msg) { notifications++; texts.push(new Text(msg)); };
    this.Read = function() {
        for(let i = 0; i < texts.length; i++) {
            if(texts[i].read || texts[i].active) { continue; }
            texts[i].active = true;
            texts[i].read = true;
            if(texts[i].id === "misctext1") { player.activeQuests["stonehenge"] = 1; }
            Sounds.PlaySound("readtext");
            notifications -= 1;
            return true;
        }
        return false;
    };
    this.Dismiss = function() {
        let count = 0;
        for(let i = 0; i < texts.length; i++) {
            if(texts[i].active) { count++; texts[i].active = false; }
        }
        if(count > 0) { Sounds.PlaySound("dismisstext"); }
        return count;
    };
    this.Draw = function() {
        if(worldmap.mapName !== "northcity") { return; }
        gfx.clearSome(["smartphone", "smartphoneText"]);
        gfx.drawTile("phoneico", 0, 192, "smartphone");
        if(notifications > 0) {
            const phoneNum = (notifications > 3) ? 4 : notifications;
            gfx.drawTile("phone" + phoneNum, 14, 194, "smartphone");
        }
        let yOffset = 0;
        for(let i = texts.length - 1; i >= 0; i--) {
            if(!texts[i].active) { continue; }
            yOffset += DrawText(texts[i].message, 11.5 - yOffset);
        }
    };
    const DrawText = function(text, y) { // maybe this should all be in rendering.js, but it's only used here, so...
        const actWidth = gfx.getTextWidth(text) + 32;
        const maxWidth = 128 * gfx.scale;
        let width = Math.min(actWidth, maxWidth);
        let xi = 1;
        const textInfo = gfx.getWrappedTextInfo(text, 124);
        let rows = textInfo.rows;
        if(rows === 1) {
            console.log("1row");
            gfx.drawWrappedText(text, 2, 10.5 + y * 16, 124, undefined, "smartphoneText");
            gfx.drawTile("selM", 0, 2 + y * 16, "smartphone");
            while(width > 128) {
                width -= 64;
                gfx.drawTile("selM", 16 * xi++, 2 + y * 16, "smartphone");
            }
            gfx.drawTile("selR", 16 * xi, 2 + y * 16, "smartphone");
            return 0.75;
        } else {
            if(rows === 2) { rows = 3; }
            if(rows >= 5) { rows = Math.ceil(textInfo.height / 9); }
            const topy = (y - rows + 1.5);
            gfx.drawWrappedText(text, 2, 12 + topy * 16, 124, undefined, "smartphoneText");
            gfx.drawTile("infoU", 0, 2 + topy * 16, "smartphone");
            gfx.drawTile("infoD", 0, 2 + (rows - 2 + topy) * 16, "smartphone");
            while(width > 128) {
                width -= 64;
                gfx.drawTile("infoU", 16 * xi, 2 + topy * 16, "smartphone");
                gfx.drawTile("infoD", 16 * xi++, 2 + (rows - 2 + topy) * 16, "smartphone");
            }
            gfx.drawTile("infoUR", 16 * xi, 2 + topy * 16, "smartphone");
            gfx.drawTile("infoDR", 16 * xi, 2 + (rows - 2 + topy) * 16, "smartphone");
            if(rows >= 4) {
                const drawRows = rows - 3;
                for(let i = 0; i < drawRows; i++) {
                    gfx.drawTile("infoR", 16 * xi, 2 + (topy + i + 1) * 16, "smartphone");
                }
                let ctx = gfx.ctx["smartphone"];
                ctx.fillStyle = player.IsMonochrome() ? "#E0F8D0" : "#B2B5FF";
                ctx.fillRect(0, 8 + (topy + 1) * 16 * gfx.scale, 112 * gfx.scale, drawRows * 16 * gfx.scale);
            }
            return rows - 0.5;
        }
    }
};<commit_msg>prevented texting outside of the city<commit_after>function Text(msg) {
    this.id = msg;
    this.message = GetText(msg);
    this.read = false;
    this.active = false;
}
function Smartphone() {
    let texts = [ new Text("beckettText0"), new Text("beckettText1"), new Text("beckettText2"), new Text("beckettText3") ];
    let notifications = texts.length;
    let remainingTexts = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], pushedLimit = false;
    let datingApp = 0, datingAppMax = 7;
    let havePushedSister = false, sisterChance = 0;
    let lastX = 0, lastY = 0;
    this.GetPhoneData = function() {
        return {
            rt: remainingTexts, t: texts,
            hps: havePushedSister, sC: sisterChance,
            dA: datingApp, pL: pushedLimit
        };
    };
    this.SetPhoneData = function(t) {
        remainingTexts = t.rt;
        texts = t.t;
        notifications = 0;
        for(let i = 0; i < texts.length; i++) { if(!texts[i].read) { notifications++; } }
        havePushedSister = t.hps;
        sisterChance = t.sC;
        datingApp = t.dA;
        pushedLimit = t.pL;
    };
    this.Update = function() {
        if(worldmap.mapName !== "northcity") { return; }
        if(Math.round(worldmap.pos.x) % 4 !== 0 && Math.round(worldmap.pos.y) % 3 !== 0) { return; }
        if(Math.random() > 0.16) { return; }
        if(worldmap.pos.x === lastX || worldmap.pos.y === lastY) { return; }
        lastX = worldmap.pos.x; lastY = worldmap.pos.y;
        worldmap.smartphone.PushRandomText();
    };
    this.PushRandomText = function() {
        if(!havePushedSister) {
            if(Math.random() >= sisterChance) {
                sisterChance += 0.05;
            } else {
                havePushedSister = true;
                worldmap.smartphone.PushText("sistext0");
                setTimeout(function() { worldmap.smartphone.PushText("sistext1"); }, 1055);
                setTimeout(function() { worldmap.smartphone.PushText("sistext2"); }, 2203);
                setTimeout(function() { worldmap.smartphone.PushText("sistext3"); }, 3527);
                return;
            }
        }
        if(datingApp <= datingAppMax && Math.random() > 0.75) {
            worldmap.smartphone.PushText("sext" + (datingApp++));
        } else {
            const result = remainingTexts.splice(Math.floor(Math.random() * remainingTexts.length), 1)[0];
            if(result !== undefined) { worldmap.smartphone.PushText("misctext" + result); }
            else if(!pushedLimit && datingApp > datingAppMax) { worldmap.smartphone.PushText("misctext14"); pushedLimit = true; }
        }
    };
    this.PushText = function(msg) { notifications++; texts.push(new Text(msg)); };
    this.Read = function() {
        if(worldmap.mapName !== "northcity") { return; }
        for(let i = 0; i < texts.length; i++) {
            if(texts[i].read || texts[i].active) { continue; }
            texts[i].active = true;
            texts[i].read = true;
            if(texts[i].id === "misctext1") { player.activeQuests["stonehenge"] = 1; }
            Sounds.PlaySound("readtext");
            notifications -= 1;
            return true;
        }
        return false;
    };
    this.Dismiss = function() {
        if(worldmap.mapName !== "northcity") { return; }
        let count = 0;
        for(let i = 0; i < texts.length; i++) {
            if(texts[i].active) { count++; texts[i].active = false; }
        }
        if(count > 0) { Sounds.PlaySound("dismisstext"); }
        return count;
    };
    this.Draw = function() {
        if(worldmap.mapName !== "northcity") { return; }
        gfx.clearSome(["smartphone", "smartphoneText"]);
        gfx.drawTile("phoneico", 0, 192, "smartphone");
        if(notifications > 0) {
            const phoneNum = (notifications > 3) ? 4 : notifications;
            gfx.drawTile("phone" + phoneNum, 14, 194, "smartphone");
        }
        let yOffset = 0;
        for(let i = texts.length - 1; i >= 0; i--) {
            if(!texts[i].active) { continue; }
            yOffset += DrawText(texts[i].message, 11.5 - yOffset);
        }
    };
    const DrawText = function(text, y) { // maybe this should all be in rendering.js, but it's only used here, so...
        const actWidth = gfx.getTextWidth(text) + 32;
        const maxWidth = 128 * gfx.scale;
        let width = Math.min(actWidth, maxWidth);
        let xi = 1;
        const textInfo = gfx.getWrappedTextInfo(text, 124);
        let rows = textInfo.rows;
        if(rows === 1) {
            console.log("1row");
            gfx.drawWrappedText(text, 2, 10.5 + y * 16, 124, undefined, "smartphoneText");
            gfx.drawTile("selM", 0, 2 + y * 16, "smartphone");
            while(width > 128) {
                width -= 64;
                gfx.drawTile("selM", 16 * xi++, 2 + y * 16, "smartphone");
            }
            gfx.drawTile("selR", 16 * xi, 2 + y * 16, "smartphone");
            return 0.75;
        } else {
            if(rows === 2) { rows = 3; }
            if(rows >= 5) { rows = Math.ceil(textInfo.height / 9); }
            const topy = (y - rows + 1.5);
            gfx.drawWrappedText(text, 2, 12 + topy * 16, 124, undefined, "smartphoneText");
            gfx.drawTile("infoU", 0, 2 + topy * 16, "smartphone");
            gfx.drawTile("infoD", 0, 2 + (rows - 2 + topy) * 16, "smartphone");
            while(width > 128) {
                width -= 64;
                gfx.drawTile("infoU", 16 * xi, 2 + topy * 16, "smartphone");
                gfx.drawTile("infoD", 16 * xi++, 2 + (rows - 2 + topy) * 16, "smartphone");
            }
            gfx.drawTile("infoUR", 16 * xi, 2 + topy * 16, "smartphone");
            gfx.drawTile("infoDR", 16 * xi, 2 + (rows - 2 + topy) * 16, "smartphone");
            if(rows >= 4) {
                const drawRows = rows - 3;
                for(let i = 0; i < drawRows; i++) {
                    gfx.drawTile("infoR", 16 * xi, 2 + (topy + i + 1) * 16, "smartphone");
                }
                let ctx = gfx.ctx["smartphone"];
                ctx.fillStyle = player.IsMonochrome() ? "#E0F8D0" : "#B2B5FF";
                ctx.fillRect(0, 8 + (topy + 1) * 16 * gfx.scale, 112 * gfx.scale, drawRows * 16 * gfx.scale);
            }
            return rows - 0.5;
        }
    }
};<|endoftext|>