<commit_before>/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the new BSD license.
 *
 * @author      David Zeller <zellerda01@gmail.com>
 * @license     http://www.opensource.org/licenses/BSD-3-Clause New BSD license
 * @version     2.0
 */
(function($, tokenize){

    // Keycodes
    var KEYS = {
        BACKSPACE: 8,
        TAB: 9,
        ENTER: 13,
        ESCAPE: 27,
        ARROW_UP: 38,
        ARROW_DOWN: 40,
        COMMA: 188
    };

    // Data storage constant
    var DATA = 'tokenize';

    $.tokenize = function(opts){

        if(opts == undefined){
            opts = $.fn.tokenize.defaults;
        }

        this.options = opts;
    };

    $.extend($.tokenize.prototype, {

        init: function(el){

            var $this = this;
            this.mouseOnContainer = false;
            this.select = el.attr('multiple', 'multiple').css({margin: 0, padding: 0, border: 0}).hide();

            this.container = $('<div />')
                .attr('class', this.select.attr('class'))
                .addClass('Tokenize');

            if(this.options.maxElements == 1){
                this.container.addClass('OnlyOne');
            }

            this.dropdown = $('<ul />')
                .addClass('Dropdown');

            this.tokensContainer = $('<ul />')
                .addClass('TokensContainer');

            this.searchToken = $('<li />')
                .addClass('TokenSearch')
                .appendTo(this.tokensContainer);

            this.searchInput = $('<input />')
                .attr('maxlength', this.options.searchMaxLength)
                .appendTo(this.searchToken);

            this.container
                .append(this.tokensContainer)
                .append(this.dropdown)
                .insertAfter(this.select);

            this.tokensContainer.bind('click', function(){
                $this.searchInput.get(0).focus();
                if($this.dropdown.is(':hidden') && $this.searchInput.val() != ''){
                    $this.search();
                }
            });

            this.searchInput.bind('keydown', function(e){
                $this.resizeSearchInput();
                $this.keydown(e);
            });

            this.searchInput.bind('keyup', function(e){
                $this.keyup(e);
            });

            this.searchInput.on('paste', function(e){
                setTimeout(function(){ $this.resizeSearchInput(); }, 10);
            });

            this.container.bind('mouseenter', function(){
                $this.mouseOnContainer = true;
            });

            this.container.bind('mouseleave', function(){
                $this.mouseOnContainer = false;
            });

            $(document).bind('click', function(){
                if(!$this.mouseOnContainer){
                    $this.dropdownHide();
                    if($this.options.maxElements == 1){
                        if($this.searchInput.val()){
                            $this.tokenAdd($this.searchInput.val(), '');
                        }
                    }
                }
            });

            this.resizeSearchInput();

            $('option:selected', this.select).each(function(){
                $this.tokenAdd($(this).attr('value'), $(this).html(), true);
            });

        },

        dropdownShow: function(){

            this.dropdown.show();

        },

        dropdownPrev: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:first-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').prev().addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownNext: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:last-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').next().addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownAddItem: function(value, text, html){

            if(html == undefined){
                html = text;
            }

            if($('li[data-value="' + value + '"]', this.tokensContainer).length){
                return false;
            }

            var $this = this;
            var item = $('<li />')
                .attr('data-value', value)
                .attr('data-text', text)
                .html(html)
                .bind('click', function(){
                    $this.tokenAdd($(this).attr('data-value'), $(this).attr('data-text'));
                }).bind('mouseover', function(){
                    $(this).addClass('Hover');
                }).bind('mouseout', function(){
                    $('li', $this.dropdown).removeClass('Hover');
                });

            this.dropdown.append(item);
            return true;

        },

        dropdownHide: function(){

            this.dropdownReset();
            this.dropdown.hide();

        },

        dropdownReset: function(){

            this.dropdown.html('');

        },

        resizeSearchInput: function(){

            var measure = $('<div />')
                .css({ position: 'absolute', visibility: 'hidden' })
                .addClass('TokenizeMeasure')
                .html(this.searchInput.val());

            $('body').append(measure);

            this.searchInput.width(measure.width() + 25);
            measure.remove();

        },

        resetSearchInput: function(){

            this.searchInput.val("");
            this.resizeSearchInput();

        },

        resetPendingTokens: function(){

            $('li.PendingDelete', this.tokensContainer).removeClass('PendingDelete');

        },

        keydown: function(e){

            if(e.keyCode == this.options.validator){
                e.preventDefault();
                this.tokenAdd(this.searchInput.val(), '');
            } else {
                switch(e.keyCode){
                    case KEYS.BACKSPACE:
                        if(this.searchInput.val().length == 0){
                            e.preventDefault();
                            if($('li.Token.PendingDelete', this.tokensContainer).length){
                                this.tokenRemove($('li.Token.PendingDelete').attr('data-value'));
                            } else {
                                $('li.Token:last', this.tokensContainer).addClass('PendingDelete');
                            }

                            this.dropdownHide();
                        }
                        break;

                    case KEYS.TAB:
                    case KEYS.ENTER:
                        if($('li.Hover', this.dropdown).length){
                            var element = $('li.Hover', this.dropdown);
                            e.preventDefault();
                            this.tokenAdd(element.attr('data-value'), element.attr('data-text'));
                        } else {
                            if(this.searchInput.val()){
                                e.preventDefault();
                                this.tokenAdd(this.searchInput.val(), '');
                            }
                        }
                        this.resetPendingTokens();
                        break;

                    case KEYS.ESCAPE:
                        this.resetSearchInput();
                        this.dropdownHide();
                        this.resetPendingTokens();
                        break;

                    case KEYS.ARROW_UP:
                        e.preventDefault();
                        this.dropdownPrev();
                        break;

                    case KEYS.ARROW_DOWN:
                        e.preventDefault();
                        this.dropdownNext();
                        break;

                    default:
                        this.resetPendingTokens();
                        break;
                }
            }

        },

        keyup: function(e){

            if(e.keyCode != this.options.validator){
                switch(e.keyCode){
                    case KEYS.TAB:
                    case KEYS.ENTER:
                    case KEYS.ESCAPE:
                    case KEYS.ARROW_UP:
                    case KEYS.ARROW_DOWN:
                        break;

                    case KEYS.BACKSPACE:
                        if(this.searchInput.val()){
                            this.search();
                        } else {
                            this.dropdownHide();
                        }
                        break;
                    default:
                        if(this.searchInput.val()){
                            this.search();
                        }
                        break;
                }
            }

        },

        search: function(){

            var $this = this;
            var count = 1;

            if(this.options.maxElements > 0 && $('li.Token', 'ul.TokensContainer').length >= this.options.maxElements){
                return false;
            }

            if(this.options.datas == 'select'){

                var found = false, regexp = new RegExp(this.searchInput.val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
                this.dropdownReset();

                $('option', this.select).not(':selected').each(function(){
                    if(count <= $this.options.nbDropdownElements){
                        if(regexp.test($(this).html())){
                            $this.dropdownAddItem($(this).attr('value'), $(this).html());
                            found = true;
                            count++;
                        }
                    } else {
                        return false;
                    }
                });

                if(found){
                    $('li:first', this.dropdown).addClass('Hover');
                    this.dropdownShow();
                } else {
                    this.dropdownHide();
                }

            } else {

                $.getJSON(this.options.datas, this.options.searchParam + "=" + this.searchInput.val(), function(data){

                    if(data){
                        var found = false;
                        $.each(data, function(key, val){
                            found = true;
                            return true;
                        });

                        if(found){
                            $this.dropdownReset();

                            $.each(data, function(key, val){
                                if(count <= $this.options.nbDropdownElements){
                                    var html = undefined;
                                    if(val.html){
                                        html = val.html;
                                    }
                                    $this.dropdownAddItem(val.value, val.text, html);
                                    count++;
                                } else {
                                    return false;
                                }
                            });

                            $('li:first', $this.dropdown).addClass('Hover');
                            $this.dropdownShow();

                            return true;
                        }
                    }

                    $this.dropdownHide();

                });

            }

        },

        tokenAdd: function(value, text, first){

            if(value == undefined || value == ''){
                return false;
            }

            if(text == undefined || text == ''){
                text = value;
            }

            if(first == undefined){
                first = false;
            }

            if(this.options.maxElements > 0 && $('li.Token', 'ul.TokensContainer').length >= this.options.maxElements){
                this.resetSearchInput();
                return false;
            }

            var $this = this;
            var close_btn = $('<a />')
                .addClass('Close')
                .html("×")
                .bind('click', function(){
                    $this.tokenRemove(value);
                });

            if($('option[value="' + value + '"]', this.select).length){
                $('option[value="' + value + '"]', this.select).attr('selected', 'selected');
            } else if(this.options.newElements) {
                var option = $('<option />')
                    .attr('selected', 'selected')
                    .attr('value', value)
                    .attr('data-type', 'custom')
                    .html(text);

                this.select.append(option);
            } else {
                this.resetSearchInput();
                return false;
            }

            if($('li.Token[data-value="' + value + '"]', this.tokensContainer).length > 0) {
                return false;
            }

            $('<li />')
                .addClass('Token')
                .attr('data-value', value)
                .append('<span>' + text + '</span>')
                .prepend(close_btn)
                .insertBefore(this.searchToken);

            if(!first){
                this.options.onAddToken(value, text);
            }

            this.resetSearchInput();
            this.dropdownHide();

            return true;

        },

        tokenRemove: function(value){

            var option = $('option[value="' + value + '"]', this.select);

            if(option.attr('data-type') == 'custom'){
                option.remove();
            } else {
                option.removeAttr('selected');
            }

            $('li.Token[data-value="' + value + '"]', this.tokensContainer).remove();

            this.options.onRemoveToken(value);
            this.resizeSearchInput();
            this.dropdownHide();

        }

    });

    $.fn.tokenize = function(options){

        if(options == undefined){
            options = {};
        }

        var opt = $.extend({}, $.fn.tokenize.defaults, options);
        var obj = new $.tokenize(opt);
        obj.init(this);

        $(this).data(DATA, obj);

        return this;

    };

    $.fn.tokenize.defaults = {

        datas: 'select',
        searchParam: 'search',
        searchMaxLength: 30,
        validator: KEYS.COMMA,
        newElements: true,
        nbDropdownElements: 10,
        maxElements: 0,

        onAddToken: function(value, text){},
        onRemoveToken: function(value){}

    };

})(jQuery, 'tokenize');<commit_msg>Change version<commit_after>/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the new BSD license.
 *
 * @author      David Zeller <zellerda01@gmail.com>
 * @license     http://www.opensource.org/licenses/BSD-3-Clause New BSD license
 * @version     2.1
 */
(function($, tokenize){

    // Keycodes
    var KEYS = {
        BACKSPACE: 8,
        TAB: 9,
        ENTER: 13,
        ESCAPE: 27,
        ARROW_UP: 38,
        ARROW_DOWN: 40,
        COMMA: 188
    };

    // Data storage constant
    var DATA = 'tokenize';

    $.tokenize = function(opts){

        if(opts == undefined){
            opts = $.fn.tokenize.defaults;
        }

        this.options = opts;
    };

    $.extend($.tokenize.prototype, {

        init: function(el){

            var $this = this;
            this.mouseOnContainer = false;
            this.select = el.attr('multiple', 'multiple').css({margin: 0, padding: 0, border: 0}).hide();

            this.container = $('<div />')
                .attr('class', this.select.attr('class'))
                .addClass('Tokenize');

            if(this.options.maxElements == 1){
                this.container.addClass('OnlyOne');
            }

            this.dropdown = $('<ul />')
                .addClass('Dropdown');

            this.tokensContainer = $('<ul />')
                .addClass('TokensContainer');

            this.searchToken = $('<li />')
                .addClass('TokenSearch')
                .appendTo(this.tokensContainer);

            this.searchInput = $('<input />')
                .attr('maxlength', this.options.searchMaxLength)
                .appendTo(this.searchToken);

            this.container
                .append(this.tokensContainer)
                .append(this.dropdown)
                .insertAfter(this.select);

            this.tokensContainer.bind('click', function(){
                $this.searchInput.get(0).focus();
                if($this.dropdown.is(':hidden') && $this.searchInput.val() != ''){
                    $this.search();
                }
            });

            this.searchInput.bind('keydown', function(e){
                $this.resizeSearchInput();
                $this.keydown(e);
            });

            this.searchInput.bind('keyup', function(e){
                $this.keyup(e);
            });

            this.searchInput.on('paste', function(e){
                setTimeout(function(){ $this.resizeSearchInput(); }, 10);
            });

            this.container.bind('mouseenter', function(){
                $this.mouseOnContainer = true;
            });

            this.container.bind('mouseleave', function(){
                $this.mouseOnContainer = false;
            });

            $(document).bind('click', function(){
                if(!$this.mouseOnContainer){
                    $this.dropdownHide();
                    if($this.options.maxElements == 1){
                        if($this.searchInput.val()){
                            $this.tokenAdd($this.searchInput.val(), '');
                        }
                    }
                }
            });

            this.resizeSearchInput();

            $('option:selected', this.select).each(function(){
                $this.tokenAdd($(this).attr('value'), $(this).html(), true);
            });

        },

        dropdownShow: function(){

            this.dropdown.show();

        },

        dropdownPrev: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:first-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').prev().addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownNext: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:last-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').next().addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownAddItem: function(value, text, html){

            if(html == undefined){
                html = text;
            }

            if($('li[data-value="' + value + '"]', this.tokensContainer).length){
                return false;
            }

            var $this = this;
            var item = $('<li />')
                .attr('data-value', value)
                .attr('data-text', text)
                .html(html)
                .bind('click', function(){
                    $this.tokenAdd($(this).attr('data-value'), $(this).attr('data-text'));
                }).bind('mouseover', function(){
                    $(this).addClass('Hover');
                }).bind('mouseout', function(){
                    $('li', $this.dropdown).removeClass('Hover');
                });

            this.dropdown.append(item);
            return true;

        },

        dropdownHide: function(){

            this.dropdownReset();
            this.dropdown.hide();

        },

        dropdownReset: function(){

            this.dropdown.html('');

        },

        resizeSearchInput: function(){

            var measure = $('<div />')
                .css({ position: 'absolute', visibility: 'hidden' })
                .addClass('TokenizeMeasure')
                .html(this.searchInput.val());

            $('body').append(measure);

            this.searchInput.width(measure.width() + 25);
            measure.remove();

        },

        resetSearchInput: function(){

            this.searchInput.val("");
            this.resizeSearchInput();

        },

        resetPendingTokens: function(){

            $('li.PendingDelete', this.tokensContainer).removeClass('PendingDelete');

        },

        keydown: function(e){

            if(e.keyCode == this.options.validator){
                e.preventDefault();
                this.tokenAdd(this.searchInput.val(), '');
            } else {
                switch(e.keyCode){
                    case KEYS.BACKSPACE:
                        if(this.searchInput.val().length == 0){
                            e.preventDefault();
                            if($('li.Token.PendingDelete', this.tokensContainer).length){
                                this.tokenRemove($('li.Token.PendingDelete').attr('data-value'));
                            } else {
                                $('li.Token:last', this.tokensContainer).addClass('PendingDelete');
                            }

                            this.dropdownHide();
                        }
                        break;

                    case KEYS.TAB:
                    case KEYS.ENTER:
                        if($('li.Hover', this.dropdown).length){
                            var element = $('li.Hover', this.dropdown);
                            e.preventDefault();
                            this.tokenAdd(element.attr('data-value'), element.attr('data-text'));
                        } else {
                            if(this.searchInput.val()){
                                e.preventDefault();
                                this.tokenAdd(this.searchInput.val(), '');
                            }
                        }
                        this.resetPendingTokens();
                        break;

                    case KEYS.ESCAPE:
                        this.resetSearchInput();
                        this.dropdownHide();
                        this.resetPendingTokens();
                        break;

                    case KEYS.ARROW_UP:
                        e.preventDefault();
                        this.dropdownPrev();
                        break;

                    case KEYS.ARROW_DOWN:
                        e.preventDefault();
                        this.dropdownNext();
                        break;

                    default:
                        this.resetPendingTokens();
                        break;
                }
            }

        },

        keyup: function(e){

            if(e.keyCode != this.options.validator){
                switch(e.keyCode){
                    case KEYS.TAB:
                    case KEYS.ENTER:
                    case KEYS.ESCAPE:
                    case KEYS.ARROW_UP:
                    case KEYS.ARROW_DOWN:
                        break;

                    case KEYS.BACKSPACE:
                        if(this.searchInput.val()){
                            this.search();
                        } else {
                            this.dropdownHide();
                        }
                        break;
                    default:
                        if(this.searchInput.val()){
                            this.search();
                        }
                        break;
                }
            }

        },

        search: function(){

            var $this = this;
            var count = 1;

            if(this.options.maxElements > 0 && $('li.Token', 'ul.TokensContainer').length >= this.options.maxElements){
                return false;
            }

            if(this.options.datas == 'select'){

                var found = false, regexp = new RegExp(this.searchInput.val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
                this.dropdownReset();

                $('option', this.select).not(':selected').each(function(){
                    if(count <= $this.options.nbDropdownElements){
                        if(regexp.test($(this).html())){
                            $this.dropdownAddItem($(this).attr('value'), $(this).html());
                            found = true;
                            count++;
                        }
                    } else {
                        return false;
                    }
                });

                if(found){
                    $('li:first', this.dropdown).addClass('Hover');
                    this.dropdownShow();
                } else {
                    this.dropdownHide();
                }

            } else {

                $.getJSON(this.options.datas, this.options.searchParam + "=" + this.searchInput.val(), function(data){

                    if(data){
                        var found = false;
                        $.each(data, function(key, val){
                            found = true;
                            return true;
                        });

                        if(found){
                            $this.dropdownReset();

                            $.each(data, function(key, val){
                                if(count <= $this.options.nbDropdownElements){
                                    var html = undefined;
                                    if(val.html){
                                        html = val.html;
                                    }
                                    $this.dropdownAddItem(val.value, val.text, html);
                                    count++;
                                } else {
                                    return false;
                                }
                            });

                            $('li:first', $this.dropdown).addClass('Hover');
                            $this.dropdownShow();

                            return true;
                        }
                    }

                    $this.dropdownHide();

                });

            }

        },

        tokenAdd: function(value, text, first){

            if(value == undefined || value == ''){
                return false;
            }

            if(text == undefined || text == ''){
                text = value;
            }

            if(first == undefined){
                first = false;
            }

            if(this.options.maxElements > 0 && $('li.Token', 'ul.TokensContainer').length >= this.options.maxElements){
                this.resetSearchInput();
                return false;
            }

            var $this = this;
            var close_btn = $('<a />')
                .addClass('Close')
                .html("×")
                .bind('click', function(){
                    $this.tokenRemove(value);
                });

            if($('option[value="' + value + '"]', this.select).length){
                $('option[value="' + value + '"]', this.select).attr('selected', 'selected');
            } else if(this.options.newElements) {
                var option = $('<option />')
                    .attr('selected', 'selected')
                    .attr('value', value)
                    .attr('data-type', 'custom')
                    .html(text);

                this.select.append(option);
            } else {
                this.resetSearchInput();
                return false;
            }

            if($('li.Token[data-value="' + value + '"]', this.tokensContainer).length > 0) {
                return false;
            }

            $('<li />')
                .addClass('Token')
                .attr('data-value', value)
                .append('<span>' + text + '</span>')
                .prepend(close_btn)
                .insertBefore(this.searchToken);

            if(!first){
                this.options.onAddToken(value, text);
            }

            this.resetSearchInput();
            this.dropdownHide();

            return true;

        },

        tokenRemove: function(value){

            var option = $('option[value="' + value + '"]', this.select);

            if(option.attr('data-type') == 'custom'){
                option.remove();
            } else {
                option.removeAttr('selected');
            }

            $('li.Token[data-value="' + value + '"]', this.tokensContainer).remove();

            this.options.onRemoveToken(value);
            this.resizeSearchInput();
            this.dropdownHide();

        }

    });

    $.fn.tokenize = function(options){

        if(options == undefined){
            options = {};
        }

        var opt = $.extend({}, $.fn.tokenize.defaults, options);
        var obj = new $.tokenize(opt);
        obj.init(this);

        $(this).data(DATA, obj);

        return this;

    };

    $.fn.tokenize.defaults = {

        datas: 'select',
        searchParam: 'search',
        searchMaxLength: 30,
        validator: KEYS.COMMA,
        newElements: true,
        nbDropdownElements: 10,
        maxElements: 0,

        onAddToken: function(value, text){},
        onRemoveToken: function(value){}

    };

})(jQuery, 'tokenize');<|endoftext|>


<commit_before>/*
	jQuery.Typograf.js

	https://github.com/Spearance/jQuery.Typograf.js
	http://www.typograf.ru/

	Copyright 2015, Evgeniy Lepeshkin

	Released under the MIT license.
	http://www.opensource.org/licenses/mit-license.php

	Version: v 0.1
	Date: Thu Mar 24, 2015
 */

(function($){
	$.fn.typograf = function(options){
		options = $.extend({
			leftQuote: "«",
			rightQuote: "»",
			handler: "",
			restor: ""
		}, options);

		return this.each(function(){
			var $this = $(this),
			    obj = $this.get(0),
			    exclude = false,
			    selected = false,
			    caretPosition = 0,
			    original;

			// exist Typograf handler
			if (options.handler) {
				original = "";

				$(options.handler).click(function(){
					if (original == "") {
						original = $this.val();
					}

					if (original) {
						if (options.restore) {
							$(options.restore).removeAttr("disabled");
						}
						typograf($this);
					}
				});

				// exist Restore handler
				if (options.restore) {
					$(options.restore).click(function(){
						$(this).attr("disabled", "true");
						$this.val(original);
					});
				}
			} else {
				// auto
				$this
					.keydown(function(e){
						// check for Ctrl/⌘
						exclude = Boolean(e.metaKey || e.ctrlKey);
					})
					.keyup(function(e){
						// check for selected text
						selected = Boolean(getSelectionText() != "");

						// exclude arrows and Ctrl/⌘ + A/C/V/X/Z
						if (
							// home, end, page up, page down
							e.which != "33" &&
							e.which != "34" &&
							e.which != "35" &&
							e.which != "36" &&

							// arrows
							e.which != "37" &&
							e.which != "38" &&
							e.which != "39" &&
							e.which != "40" &&

								// single Shift
							e.which != "16" &&

								// select all, copy, paste, cut, undo
							!(exclude && (e.which == "65" ||
							              e.which == "67" ||
							              e.which == "86" ||
							              e.which == "88" ||
							              e.which == "90")) &&

								// selected text
							!selected
						){
							typograf($this);
						}
						return true;
					}
				);
			}

			function typograf (textField){
				var text = textField.val();

				caretPosition = getCaretPosition(obj);

				text
					// Dash
					.replace(/(^|\n|\s|>)\-(\s)/g, "$1—$2")

					// Minus
					.replace(/\-{2} /g, function(){
						caretPosition--;
						return "— ";
					})

					// HTML-comment
					.replace(/<!—/ig, function(){
						caretPosition++;
						return "<!--";
					})

					// Numerical interval
					.replace(/(\d)( )?[-—]( )?(\d)/g, function(str, $1, $2, $3, $4){
						if ($2 == " ") { caretPosition-- }
						if ($3 == " ") { caretPosition-- }
						return $1 + "–" + $4;
					})

					// Copyright
					.replace(/\([cс]\)/ig, function(){
						caretPosition -= 2;
						return "©";
					})

					// Registered trademark
					.replace(/\(r\)/ig, function(){
						caretPosition -= 2;
						return "®";
					})

					// Trademark
					.replace(/\(tm\)/ig, function(){
						caretPosition -= 2;
						return "™";
					})

					// Rouble
					.replace(/\([рp]\)/ig, function(){
						caretPosition -= 2;
						return "₽";
					})

					// Three dots
					.replace(/\.{3}/g, function(){
						caretPosition -= 2;
						return "…";
					})

					// Sizes
					.replace(/(\d)[xх](\d)/ig, "$1×$2")

					// Open quote
					.replace(/\"([a-zа-я0-9…])/ig,
					         options.leftQuote + "$1")

					// Close quote
					.replace(/([a-zа-я0-9…?!])\"/ig,
					         "$1" + options.rightQuote)

					// Open quote
					.replace(new RegExp("\"(" + options.leftQuote + "[a-zа-я0-9…])", "ig"),
					         options.leftQuote + "$1")

					// Close quote
					.replace(new RegExp("([a-zа-я0-9…?!]" + options.rightQuote + ")\"", "ig"),
					         "$1" + options.rightQuote)

					// Fix HTML open quotes
					.replace(new RegExp("([-a-z0-9]+=)" +
					                    "["   + options.leftQuote + options.rightQuote + "]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)", "ig"),
					         "$1\"$2")

					// Fix HTML close quotes
					.replace(new RegExp("([-a-z0-9]+=)[\"]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)" +
					                    "["   + options.leftQuote + options.rightQuote + "]", "ig"),
					         "$1\"$2\"");

				textField.val(text);

				if (!exclude) {
					setCaretPosition(obj, caretPosition);
				}
			}

		});

		function getCaretPosition(ctrl) {
			var CaretPos = 0, // IE Support
			    Sel;

			if (document.selection) {
				ctrl.focus();
				Sel = document.selection.createRange();
				Sel.moveStart('character', -ctrl.value.length);
				CaretPos = Sel.text.length;
			}

			// Firefox support
			else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
				CaretPos = ctrl.selectionStart;
			}

			return CaretPos;
		}

		function setCaretPosition(ctrl, pos){
			if (ctrl.setSelectionRange) {
				ctrl.focus();
				ctrl.setSelectionRange(pos, pos);
			} else if (ctrl.createTextRange) {
				var range = ctrl.createTextRange();
				range.collapse(true);
				range.moveEnd("character", pos);
				range.moveStart("character", pos);
				range.select();
			}
		}

		function getSelectionText(){
			var t = "";

			if (window.getSelection) {
				t = window.getSelection();
			} else if (document.getSelection) {
				t = document.getSelection();
			} else if (document.selection) {
				t = document.selection.createRange().text;
			}

			return t;
		}
	};
})(jQuery);<commit_msg>Fix text raplace bug<commit_after>/*
	jQuery.Typograf.js

	https://github.com/Spearance/jQuery.Typograf.js
	http://www.typograf.ru/

	Copyright 2015, Evgeniy Lepeshkin

	Released under the MIT license.
	http://www.opensource.org/licenses/mit-license.php

	Version: v 0.1
	Date: Thu Mar 24, 2015
 */

(function($){
	$.fn.typograf = function(options){
		options = $.extend({
			leftQuote: "«",
			rightQuote: "»",
			handler: "",
			restor: ""
		}, options);

		return this.each(function(){
			var $this = $(this),
			    obj = $this.get(0),
			    exclude = false,
			    selected = false,
			    caretPosition = 0,
			    original;

			// exist Typograf handler
			if (options.handler) {
				original = "";

				$(options.handler).click(function(){
					if (original == "") {
						original = $this.val();
					}

					if (original) {
						if (options.restore) {
							$(options.restore).removeAttr("disabled");
						}
						typograf($this);
					}
				});

				// exist Restore handler
				if (options.restore) {
					$(options.restore).click(function(){
						$(this).attr("disabled", "true");
						$this.val(original);
					});
				}
			} else {
				// auto
				$this
					.keydown(function(e){
						// check for Ctrl/⌘
						exclude = Boolean(e.metaKey || e.ctrlKey);
					})
					.keyup(function(e){
						// check for selected text
						selected = Boolean(getSelectionText() != "");

						// exclude arrows and Ctrl/⌘ + A/C/V/X/Z
						if (
							// home, end, page up, page down
							e.which != "33" &&
							e.which != "34" &&
							e.which != "35" &&
							e.which != "36" &&

							// arrows
							e.which != "37" &&
							e.which != "38" &&
							e.which != "39" &&
							e.which != "40" &&

								// single Shift
							e.which != "16" &&

								// select all, copy, paste, cut, undo
							!(exclude && (e.which == "65" ||
							              e.which == "67" ||
							              e.which == "86" ||
							              e.which == "88" ||
							              e.which == "90")) &&

								// selected text
							!selected
						){
							typograf($this);
						}
						return true;
					}
				);
			}

			function typograf (textField){
				var text = textField.val();
				caretPosition = getCaretPosition(obj);

				text = text
					// Dash
					.replace(/(^|\n|\s|>)\-(\s)/g, "$1—$2")

					// Minus
					.replace(/\-{2} /g, function(){
						caretPosition--;
						return "— ";
					})

					// HTML-comment
					.replace(/<!—/ig, function(){
						caretPosition++;
						return "<!--";
					})

					// Numerical interval
					.replace(/(\d)( )?[-—]( )?(\d)/g, function(str, $1, $2, $3, $4){
						if ($2 == " ") { caretPosition-- }
						if ($3 == " ") { caretPosition-- }
						return $1 + "–" + $4;
					})

					// Copyright
					.replace(/\([cс]\)/ig, function(){
						caretPosition -= 2;
						return "©";
					})

					// Registered trademark
					.replace(/\(r\)/ig, function(){
						caretPosition -= 2;
						return "®";
					})

					// Trademark
					.replace(/\(tm\)/ig, function(){
						caretPosition -= 2;
						return "™";
					})

					// Rouble
					.replace(/\([рp]\)/ig, function(){
						caretPosition -= 2;
						return "₽";
					})

					// Three dots
					.replace(/\.{3}/g, function(){
						caretPosition -= 2;
						return "…";
					})

					// Sizes
					.replace(/(\d)[xх](\d)/ig, "$1×$2")

					// Open quote
					.replace(/\"([a-zа-я0-9…])/ig,
					         options.leftQuote + "$1")

					// Close quote
					.replace(/([a-zа-я0-9…?!])\"/ig,
					         "$1" + options.rightQuote)

					// Open quote
					.replace(new RegExp("\"(" + options.leftQuote + "[a-zа-я0-9…])", "ig"),
					         options.leftQuote + "$1")

					// Close quote
					.replace(new RegExp("([a-zа-я0-9…?!]" + options.rightQuote + ")\"", "ig"),
					         "$1" + options.rightQuote)

					// Fix HTML open quotes
					.replace(new RegExp("([-a-z0-9]+=)" +
					                    "["   + options.leftQuote + options.rightQuote + "]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)", "ig"),
					         "$1\"$2")

					// Fix HTML close quotes
					.replace(new RegExp("([-a-z0-9]+=)[\"]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)" +
					                    "["   + options.leftQuote + options.rightQuote + "]", "ig"),
					         "$1\"$2\"");

				textField.val(text);

				if (!exclude) {
					setCaretPosition(obj, caretPosition);
				}
			}

		});

		function getCaretPosition(ctrl) {
			var CaretPos = 0, // IE Support
			    Sel;

			if (document.selection) {
				ctrl.focus();
				Sel = document.selection.createRange();
				Sel.moveStart('character', -ctrl.value.length);
				CaretPos = Sel.text.length;
			}

			// Firefox support
			else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
				CaretPos = ctrl.selectionStart;
			}

			return CaretPos;
		}

		function setCaretPosition(ctrl, pos){
			if (ctrl.setSelectionRange) {
				ctrl.focus();
				ctrl.setSelectionRange(pos, pos);
			} else if (ctrl.createTextRange) {
				var range = ctrl.createTextRange();
				range.collapse(true);
				range.moveEnd("character", pos);
				range.moveStart("character", pos);
				range.select();
			}
		}

		function getSelectionText(){
			var t = "";

			if (window.getSelection) {
				t = window.getSelection();
			} else if (document.getSelection) {
				t = document.getSelection();
			} else if (document.selection) {
				t = document.selection.createRange().text;
			}

			return t;
		}
	};
})(jQuery);<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Collapsible box that show/hides contents when expanded/collapsed.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Imports
  var ExpandCollapseButton = require( 'SUN/ExpandCollapseButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Property = require( 'AXON/Property' );
  var Shape = require( 'KITE/Shape' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );

  // Constants
  var TITLE_X_MARGIN = 10;
  var BUTTON_SIZE = 16; // Can make this an option if desired.

  /**
   * @param {Node} contentNode that will be shown or hidden as the accordion box is expanded/collapsed.
   * @param {*} options Various key-value pairs that control the appearance and behavior.  Some options are
   * specific to this class while some are passed to the superclass.  See the code where the options are set in the
   * early portion of the constructor for details.
   * @constructor
   */
  function AccordionBox( contentNode, options ) {

    options = _.extend( {

      // box
      stroke: 'black',
      lineWidth: 1,
      fill: 'rgb( 238, 238, 238 )',
      cornerRadius: 3,
      initiallyExpanded: true,

      // title
      title: undefined, // title {string}  //TODO support {Node}
      font: new PhetFont( 20 ),
      titleAlign: 'center', // horizontal alignment of the title, 'left'|'center'|'right'
      titleFill: 'black',
      showTitleWhenExpanded: true,

      // expand/collapse button
      buttonAlign: 'left',  // button alignment, 'left'|'right'
      buttonXMargin: 4,
      buttonYMargin: 4,

      // content
      contentAlign: 'center', // horizontal alignment of the content, 'left'|'center'|'right'
      contentXMargin: 15,
      contentYMargin: 8

    }, options );
    this.options = options; // @private

    var thisNode = this;
    Node.call( this, options );

    // Create a property that tracks the expanded/collapsed state.
    this.expandedProperty = new Property( options.initiallyExpanded );

    // @private Create the expand/collapse button.
    this.expandCollapseButton = new ExpandCollapseButton( this.expandedProperty, { sideLength: BUTTON_SIZE } );

    // Add an expanded touch area to the expand/collapse button so it works well on small screens.   Size could be
    // moved into an option if necessary.
    var expandedTouchAreaDimension = BUTTON_SIZE * 3;
    this.expandCollapseButton.touchArea = Shape.rectangle(
        -expandedTouchAreaDimension / 2 + BUTTON_SIZE / 2,
        -expandedTouchAreaDimension / 2 + BUTTON_SIZE / 2,
      expandedTouchAreaDimension,
      expandedTouchAreaDimension
    );

    // Create the titleNode, if present.
    var titleNode = new Node();
    if ( options.title !== undefined ) {
      titleNode = new Text( options.title, { font: options.font, fill: options.titleFill } );
      this.titleNode = titleNode;
    }

    // @private Create the box that will hold the contents when expanded.
    this.boxWidth = Math.max( options.minWidth || 0, options.buttonXMargin * 2 + BUTTON_SIZE + TITLE_X_MARGIN * 2 + titleNode.width );
    if ( options.showTitleWhenExpanded ) {
      this.boxWidth = Math.max( this.boxWidth, contentNode.width + 2 * options.contentXMargin );
    }
    else {
      this.boxWidth = Math.max( this.boxWidth, options.buttonXMargin * 2 + BUTTON_SIZE + options.contentXMargin * 2 + contentNode.width );
    }
    var collapsedBoxHeight = options.buttonYMargin * 2 + BUTTON_SIZE;
    var expandedBoxHeight = 2 * options.contentYMargin + contentNode.height;
    if ( options.showTitleWhenExpanded ) {
      expandedBoxHeight += options.buttonYMargin * 2 + BUTTON_SIZE;
    }
    this.expandedHeight = expandedBoxHeight; // @public This needs to be visible externally for layout purposes.

    var expandedBox = new Rectangle( 0, 0, this.boxWidth, expandedBoxHeight, options.cornerRadius, options.cornerRadius,
      {
        stroke: options.stroke,
        lineWidth: options.lineWidth,
        fill: options.fill
      } );
    expandedBox.addChild( contentNode );
    expandedBox.addChild( titleNode );
    expandedBox.addChild( this.expandCollapseButton );
    this.addChild( expandedBox );

    // Create the node that represents the collapsed box.
    var collapsedBox = new Rectangle( 0, 0, this.boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius,
      {
        stroke: options.stroke,
        lineWidth: options.lineWidth,
        fill: options.fill
      } );
    collapsedBox.addChild( titleNode );
    collapsedBox.addChild( this.expandCollapseButton );
    this.addChild( collapsedBox );

    // If necessary, scale titleNode to fit in available space.
    this.adjustTitleNodeSize();

    // Create an invisible rectangle that allows the user to click on any part of the top of the box (expanded or
    // collapsed) in order to toggle the state.
    var expandCollapseBar = new Rectangle( 0, 0, this.boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius,
      {
        fill: 'rgba( 0, 0, 0, 0)', // Invisible.
        cursor: 'pointer'
      } );
    expandCollapseBar.addInputListener( {down: function() { thisNode.expandedProperty.set( !thisNode.expandedProperty.get() ); }} );
    expandedBox.addChild( expandCollapseBar );
    collapsedBox.addChild( expandCollapseBar );

    // Lay out the contents of the boxes.
    this.expandCollapseButton.top = options.buttonYMargin;
    this.titleLeftBound = TITLE_X_MARGIN;
    this.titleRightBound = this.boxWidth - TITLE_X_MARGIN;
    contentNode.bottom = expandedBoxHeight - options.contentYMargin;

    if ( options.buttonAlign === 'left' ) {
      this.expandCollapseButton.left = options.buttonXMargin;
      this.titleLeftBound = this.expandCollapseButton.right + TITLE_X_MARGIN;
    }
    else {
      this.expandCollapseButton.right = this.boxWidth - options.buttonXMargin;
      this.titleLeftBound = TITLE_X_MARGIN;
    }

    var contentXSpanMin = options.contentXMargin;
    var contentXSpanMax = this.boxWidth - options.contentXMargin;
    if ( !options.showTitleWhenExpanded && options.buttonAlign === 'left' ) {
      if ( options.buttonAlign === 'left' ) {
        contentXSpanMin += options.buttonXMargin * 2 + BUTTON_SIZE;
      }
      else if ( options.buttonAlign === 'left' ) {
        contentXSpanMax -= options.buttonXMargin * 2 + BUTTON_SIZE;
      }
    }
    if ( options.contentAlign === 'left' ) {
      contentNode.left = contentXSpanMin;
    }
    else if ( options.contentAlign === 'right' ) {
      contentNode.right = contentXSpanMax;
    }
    else {
      contentNode.centerX = ( contentXSpanMin + contentXSpanMax ) / 2;
    }

    this.updateTitleLocation();

    // Update the visibility of the boxes and title based on the expanded/collapsed state.
    this.expandedProperty.link( function( expanded ) {
      expandedBox.visible = expanded;
      collapsedBox.visible = !expanded;
      titleNode.visible = !expanded || options.showTitleWhenExpanded;
    } );
  }

  return inherit( Node, AccordionBox, {
    set title( t ) {
      this.titleNode.text = t;
      this.adjustTitleNodeSize();
      this.updateTitleLocation();
    },

    get title() {
      return this.options.title;
    },

    updateTitleLocation: function() {
      this.titleNode.centerY = this.expandCollapseButton.centerY;
      if ( this.options.titleAlign === 'left' ) {
        this.titleNode.left = this.titleLeftBound;
      }
      else if ( this.options.titleAlign === 'right' ) {
        this.titleNode.right = this.titleRightBound;
      }
      else {
        this.titleNode.centerX = ( this.titleLeftBound + this.titleRightBound ) / 2;
      }
    },

    //TODO this is currently ignoring scale issues in the y dimension
    adjustTitleNodeSize: function() {
      this.titleNode.resetTransform();
      var availableTitleSpace = this.boxWidth - this.options.buttonXMargin - BUTTON_SIZE - 2 * TITLE_X_MARGIN;
      if ( this.titleNode.width > availableTitleSpace ) {
        this.titleNode.scale( availableTitleSpace / this.titleNode.width );
      }
    }
  } );
} );<commit_msg>phetsims/sun#115 change API for expandProperty, pass it in as an option, update all clients<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Collapsible box that show/hides contents when expanded/collapsed.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Imports
  var ExpandCollapseButton = require( 'SUN/ExpandCollapseButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Property = require( 'AXON/Property' );
  var Shape = require( 'KITE/Shape' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );

  // Constants
  var TITLE_X_MARGIN = 10;
  var BUTTON_SIZE = 16; // Can make this an option if desired.

  /**
   * @param {Node} contentNode that will be shown or hidden as the accordion box is expanded/collapsed.
   * @param {*} options Various key-value pairs that control the appearance and behavior.  Some options are
   * specific to this class while some are passed to the superclass.  See the code where the options are set in the
   * early portion of the constructor for details.
   * @constructor
   */
  function AccordionBox( contentNode, options ) {

    options = _.extend( {

      // box
      stroke: 'black',
      lineWidth: 1,
      fill: 'rgb( 238, 238, 238 )',
      cornerRadius: 3,

      // title
      title: undefined, // title {string}  //TODO support {Node}
      font: new PhetFont( 20 ),
      titleAlign: 'center', // horizontal alignment of the title, 'left'|'center'|'right'
      titleFill: 'black',
      showTitleWhenExpanded: true,

      // expand/collapse button
      buttonAlign: 'left',  // button alignment, 'left'|'right'
      buttonXMargin: 4,
      buttonYMargin: 4,
      expandedProperty: new Property( true ),

      // content
      contentAlign: 'center', // horizontal alignment of the content, 'left'|'center'|'right'
      contentXMargin: 15,
      contentYMargin: 8

    }, options );
    this.options = options; // @private

    var thisNode = this;
    Node.call( this, options );

    // @private Create the expand/collapse button.
    this.expandCollapseButton = new ExpandCollapseButton( options.expandedProperty, { sideLength: BUTTON_SIZE } );

    // Add an expanded touch area to the expand/collapse button so it works well on small screens.   Size could be
    // moved into an option if necessary.
    var expandedTouchAreaDimension = BUTTON_SIZE * 3;
    this.expandCollapseButton.touchArea = Shape.rectangle(
        -expandedTouchAreaDimension / 2 + BUTTON_SIZE / 2,
        -expandedTouchAreaDimension / 2 + BUTTON_SIZE / 2,
      expandedTouchAreaDimension,
      expandedTouchAreaDimension
    );

    // Create the titleNode, if present.
    var titleNode = new Node();
    if ( options.title !== undefined ) {
      titleNode = new Text( options.title, { font: options.font, fill: options.titleFill } );
      this.titleNode = titleNode;
    }

    // @private Create the box that will hold the contents when expanded.
    this.boxWidth = Math.max( options.minWidth || 0, options.buttonXMargin * 2 + BUTTON_SIZE + TITLE_X_MARGIN * 2 + titleNode.width );
    if ( options.showTitleWhenExpanded ) {
      this.boxWidth = Math.max( this.boxWidth, contentNode.width + 2 * options.contentXMargin );
    }
    else {
      this.boxWidth = Math.max( this.boxWidth, options.buttonXMargin * 2 + BUTTON_SIZE + options.contentXMargin * 2 + contentNode.width );
    }
    var collapsedBoxHeight = options.buttonYMargin * 2 + BUTTON_SIZE;
    var expandedBoxHeight = 2 * options.contentYMargin + contentNode.height;
    if ( options.showTitleWhenExpanded ) {
      expandedBoxHeight += options.buttonYMargin * 2 + BUTTON_SIZE;
    }
    this.expandedHeight = expandedBoxHeight; // @public This needs to be visible externally for layout purposes.

    var expandedBox = new Rectangle( 0, 0, this.boxWidth, expandedBoxHeight, options.cornerRadius, options.cornerRadius,
      {
        stroke: options.stroke,
        lineWidth: options.lineWidth,
        fill: options.fill
      } );
    expandedBox.addChild( contentNode );
    expandedBox.addChild( titleNode );
    expandedBox.addChild( this.expandCollapseButton );
    this.addChild( expandedBox );

    // Create the node that represents the collapsed box.
    var collapsedBox = new Rectangle( 0, 0, this.boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius,
      {
        stroke: options.stroke,
        lineWidth: options.lineWidth,
        fill: options.fill
      } );
    collapsedBox.addChild( titleNode );
    collapsedBox.addChild( this.expandCollapseButton );
    this.addChild( collapsedBox );

    // If necessary, scale titleNode to fit in available space.
    this.adjustTitleNodeSize();

    // Create an invisible rectangle that allows the user to click on any part of the top of the box (expanded or
    // collapsed) in order to toggle the state.
    var expandCollapseBar = new Rectangle( 0, 0, this.boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius,
      {
        fill: 'rgba( 0, 0, 0, 0)', // Invisible.
        cursor: 'pointer'
      } );
    expandCollapseBar.addInputListener( {down: function() { options.expandedProperty.set( !options.expandedProperty.get() ); }} );
    expandedBox.addChild( expandCollapseBar );
    collapsedBox.addChild( expandCollapseBar );

    // Lay out the contents of the boxes.
    this.expandCollapseButton.top = options.buttonYMargin;
    this.titleLeftBound = TITLE_X_MARGIN;
    this.titleRightBound = this.boxWidth - TITLE_X_MARGIN;
    contentNode.bottom = expandedBoxHeight - options.contentYMargin;

    if ( options.buttonAlign === 'left' ) {
      this.expandCollapseButton.left = options.buttonXMargin;
      this.titleLeftBound = this.expandCollapseButton.right + TITLE_X_MARGIN;
    }
    else {
      this.expandCollapseButton.right = this.boxWidth - options.buttonXMargin;
      this.titleLeftBound = TITLE_X_MARGIN;
    }

    var contentXSpanMin = options.contentXMargin;
    var contentXSpanMax = this.boxWidth - options.contentXMargin;
    if ( !options.showTitleWhenExpanded && options.buttonAlign === 'left' ) {
      if ( options.buttonAlign === 'left' ) {
        contentXSpanMin += options.buttonXMargin * 2 + BUTTON_SIZE;
      }
      else if ( options.buttonAlign === 'left' ) {
        contentXSpanMax -= options.buttonXMargin * 2 + BUTTON_SIZE;
      }
    }
    if ( options.contentAlign === 'left' ) {
      contentNode.left = contentXSpanMin;
    }
    else if ( options.contentAlign === 'right' ) {
      contentNode.right = contentXSpanMax;
    }
    else {
      contentNode.centerX = ( contentXSpanMin + contentXSpanMax ) / 2;
    }

    this.updateTitleLocation();

    // Update the visibility of the boxes and title based on the expanded/collapsed state.
    options.expandedProperty.link( function( expanded ) {
      expandedBox.visible = expanded;
      collapsedBox.visible = !expanded;
      titleNode.visible = !expanded || options.showTitleWhenExpanded;
    } );
  }

  return inherit( Node, AccordionBox, {
    set title( t ) {
      this.titleNode.text = t;
      this.adjustTitleNodeSize();
      this.updateTitleLocation();
    },

    get title() {
      return this.options.title;
    },

    updateTitleLocation: function() {
      this.titleNode.centerY = this.expandCollapseButton.centerY;
      if ( this.options.titleAlign === 'left' ) {
        this.titleNode.left = this.titleLeftBound;
      }
      else if ( this.options.titleAlign === 'right' ) {
        this.titleNode.right = this.titleRightBound;
      }
      else {
        this.titleNode.centerX = ( this.titleLeftBound + this.titleRightBound ) / 2;
      }
    },

    //TODO this is currently ignoring scale issues in the y dimension
    adjustTitleNodeSize: function() {
      this.titleNode.resetTransform();
      var availableTitleSpace = this.boxWidth - this.options.buttonXMargin - BUTTON_SIZE - 2 * TITLE_X_MARGIN;
      if ( this.titleNode.width > availableTitleSpace ) {
        this.titleNode.scale( availableTitleSpace / this.titleNode.width );
      }
    }
  } );
} );<|endoftext|>


<commit_before>(function() {

    // Dependencies Check first
    var dependencies = [
        "Getter",
        "KeyMap"
    ];

    for (var i = 0, len = dependencies.length; i < len; i++) {
        if (typeof window[dependencies[i]] === "undefined") {
            console.error("AutoComplete depends on following libraries : " + dependencies.join(" "));
            console.error("Please Load them before using it");
            return false;
        }
    }

    // TODO
    // refactor helpers to different places
    // and rewrite $.* functions for multiple selectors
    var $ = function(sel) {
        return document.querySelectorAll(sel);
    };

    $.removeClass = function(sel, className) {

        var $sel;

        if (typeof sel === "string") {
            $sel = $(sel);
        }
        else {
            $sel = [sel];
        }

        for (var i = 0, len = $sel.length; i < len; i++) {
            if ($.hasClass($sel[i], className)) {

                // check all possible classnames
                var regex = new RegExp("\\s*" + className + "\\s*", "g"),
                    newClassName = $sel[i].className.replace(regex, " ");

                $sel[i].className = newClassName;
            }
        }
    };

    $.addClass = function(sel, className) {

        var $sel;

        if (typeof sel === "string") {
            $sel = $(sel);
        }
        else {
            $sel = [sel];
        }

        for (var i = 0, len = $sel.length; i < len; i++) {
            if (!$.hasClass($sel[i], className)) {
                $sel[i].className += " " + className;
            }
        }
    };

    $.hasClass = function(sel, className) {

        // hasClass is only used in single selector

        var $sel;

        if (typeof sel === "string") {
            $sel = $(sel)[0];
        }
        else {
            $sel = [sel][0];
        }

        if ($sel) {
            var regex = new RegExp(className);
            return regex.test($sel.className);
        }

        return false;
    };

    $.siblings = function(allSel, targetSel) {

        var doms = $(allSel),
            targetDom = $(targetSel)[0],
            nextSibling = null,
            prevSibling = null;

        for (var i = 0, len = doms.length; i < len; i++) {

            // matched
            if (doms[i] == targetDom) {
                if (typeof doms[i-1] !== "undefined") {
                    prevSibling = doms[i-1];
                }
                if (typeof doms[i+1] !== "undefined") {
                    nextSibling = doms[i+1];
                }
            }
        }

        return {
            next : nextSibling,
            prev : prevSibling
        };
    };

    var AutoComplete = function(targetSelector, userOptions) {

        // options
        userOptions = userOptions || {};

        // internal values
        this.doms = $(targetSelector);

        this.autoCompleteData = userOptions.data || null;
        this.autoCompleteDataSrc = userOptions.dataSrc || "data/autocomplete.json";
        this.autoCompleteSearchTimer = null;
        this.autoCompleteSearchDelay = userOptions.delay || 10;
        this.autoCompleteWrapperClass = "autocomplete-wrapper";
        this.autoCompleteListClass = "autocomplete-list"
        this.autoCompleteOpening = false;
        this.autoCompleteMatchedData = [];

        // start point
        this.init();
    };

    AutoComplete.prototype = {

        init : function() {

            // get data first
            this.getAutocompleteData(function() {

                this.createAutocompleteWrapper();

                this.bindInputSearchEvent();

            });
        },

        createAutocompleteWrapper : function() {
            if ( $("." + this.autoCompleteWrapperClass).length === 0 ) {

                var wrapperDiv = document.createElement("div");
                wrapperDiv.className = this.autoCompleteWrapperClass + " close";

                $("body")[0].appendChild(wrapperDiv);
            }
        },

        createAutocompleteList : function() {

            var fragment = document.createDocumentFragment(),
                matches = this.autoCompleteMatchedData;

            for (var i = 0, len = matches.length; i < len; i++) {

                var match = matches[i],
                    eachListDiv = document.createElement("div");

                eachListDiv.innerHTML = match;
                eachListDiv.className = this.autoCompleteListClass;

                // select the first one
                if (i === 0) {
                    eachListDiv.className += " selected";
                }

                fragment.appendChild(eachListDiv);
            }

            $("." + this.autoCompleteWrapperClass)[0].appendChild(fragment);
        },

        removeAutocompleteList : function() {
            var $wrapper = $("." + this.autoCompleteWrapperClass)[0];

            while($wrapper.firstChild) {
                $wrapper.removeChild($wrapper.firstChild);
            }
        },

        showAutocompleteWrapper : function() {
            if (!this.autoCompleteOpening) {
                $.removeClass("." + this.autoCompleteWrapperClass, "close");
            }
            this.autoCompleteOpening = true;
        },

        hideAutocompleteWrapper : function() {
            if (this.autoCompleteOpening) {
                $.addClass("." + this.autoCompleteWrapperClass, "close");
            }
            this.autoCompleteOpening = false;
        },

        repositionAutocompleteWrapper : function(dom) {

            var VISUAL_OFFSET = 5,
                rect = dom.getBoundingClientRect(),
                $wrapper = $("." + this.autoCompleteWrapperClass)[0];

            $wrapper.style.top = parseInt(rect.top + rect.height + VISUAL_OFFSET, 10) + "px";
            $wrapper.style.left = parseInt(rect.left, 10) + "px";
        },

        getAutocompleteData : function(callback) {

            var that = this;

            // we can pass autoCompleteData from userOptions directly or fetch from a link
            if ( this.autoCompleteData === null ) {
                Getter(this.autoCompleteDataSrc, function(data) {
                    // TODO
                    // check data here
                    var parsedData = JSON.parse(data);
                    that.autoCompleteData = parsedData["autoCompleteData"];

                    callback.call(that);
                });
            }
            else {
                callback.call(that);
            }
        },

        bindInputSearchEvent : function() {

            var that = this;

            for (var i = 0, len = this.doms.length; i < len; i++) {

                var dom = this.doms[i];

                dom.onkeydown = function(e) {

                    if (KeyMap[e.which] === "ENTER") {
                        that.selectMatchedData();
                    }
                    else if (KeyMap[e.which] === "UP") {
                        that.moveAutocompleteSelector("UP");
                        e.preventDefault()
                    }
                    else if (KeyMap[e.which] === "DOWN") {
                        that.moveAutocompleteSelector("DOWN");
                        e.preventDefault()
                    }
                    else if (KeyMap[e.which] === "ESC") {
                        that.hideAutocompleteWrapper();
                    }
                    else {

                        window.clearTimeout(that.autoCompleteSearchTimer);
                        that.autoCompleteSearchTimer = window.setTimeout(function() {

                            var userInputValue = that.trimSpaces(dom.value);
                            that.autoCompleteMatchedData = that.searchPossibleMatches(userInputValue);

                            if (that.isMatched()) {

                                // remove them first
                                that.removeAutocompleteList();
                                that.createAutocompleteList();
                                that.repositionAutocompleteWrapper(dom);
                                that.showAutocompleteWrapper();
                            }
                            else {
                                that.hideAutocompleteWrapper();
                            }

                        }, that.autoCompleteSearchDelay);

                    }
                };

                dom.onfocus = function(e) {
                    // because they are still left in the DOM tree
                    if (that.isMatched()) {
                        that.repositionAutocompleteWrapper(dom);
                        that.showAutocompleteWrapper();
                    }
                };

                dom.onblur = function(e) {
                    that.hideAutocompleteWrapper();
                };
            }
        },

        trimSpaces : function(userInputValue) {
            return userInputValue.replace(/^\s\s*/, '').replace(/\s\s*$/,'');
        },

        searchPossibleMatches : function(userInputValue) {

            if (userInputValue.length === 0) {
                return [];
            }

            var matches = [],
                regex = new RegExp("^" + userInputValue, "i");

            for (var i = 0, len = this.autoCompleteData.length; i < len; i++) {

                var currentAcData = this.autoCompleteData[i];

                if (regex.test(currentAcData)) {
                    matches.push(currentAcData);
                }
            }

            return matches;
        },

        moveAutocompleteSelector : function(direction) {

            var siblings = $.siblings("." + this.autoCompleteListClass, "." + this.autoCompleteListClass + ".selected");

            // make sure we have siblings so that we can move selector
            if (siblings.prev && direction === "UP" || siblings.next && direction === "DOWN" ) {

                $.removeClass("." + this.autoCompleteListClass, "selected");

                if (direction === "UP") {
                    $.addClass(siblings.prev, "selected");
                }
                else {
                    $.addClass(siblings.next, "selected");
                }
            }
        },

        selectMatchedData : function() {

        },

        isMatched : function() {
            return (this.autoCompleteMatchedData.length > 0);
        }

    };

    window.AutoComplete = AutoComplete;
}());
<commit_msg>can select autocomplete from keyboard (enter) now<commit_after>(function() {

    // Dependencies Check first
    var dependencies = [
        "Getter",
        "KeyMap"
    ];

    for (var i = 0, len = dependencies.length; i < len; i++) {
        if (typeof window[dependencies[i]] === "undefined") {
            console.error("AutoComplete depends on following libraries : " + dependencies.join(" "));
            console.error("Please Load them before using it");
            return false;
        }
    }

    // TODO
    // refactor helpers to different places
    // and rewrite $.* functions for multiple selectors
    var $ = function(sel) {
        return document.querySelectorAll(sel);
    };

    $.removeClass = function(sel, className) {

        var $sel;

        if (typeof sel === "string") {
            $sel = $(sel);
        }
        else {
            $sel = [sel];
        }

        for (var i = 0, len = $sel.length; i < len; i++) {
            if ($.hasClass($sel[i], className)) {

                // check all possible classnames
                var regex = new RegExp("\\s*" + className + "\\s*", "g"),
                    newClassName = $sel[i].className.replace(regex, " ");

                $sel[i].className = newClassName;
            }
        }
    };

    $.addClass = function(sel, className) {

        var $sel;

        if (typeof sel === "string") {
            $sel = $(sel);
        }
        else {
            $sel = [sel];
        }

        for (var i = 0, len = $sel.length; i < len; i++) {
            if (!$.hasClass($sel[i], className)) {
                $sel[i].className += " " + className;
            }
        }
    };

    $.hasClass = function(sel, className) {

        // hasClass is only used in single selector

        var $sel;

        if (typeof sel === "string") {
            $sel = $(sel)[0];
        }
        else {
            $sel = [sel][0];
        }

        if ($sel) {
            var regex = new RegExp(className);
            return regex.test($sel.className);
        }

        return false;
    };

    $.siblings = function(allSel, targetSel) {

        var doms = $(allSel),
            targetDom = $(targetSel)[0],
            nextSibling = null,
            prevSibling = null;

        for (var i = 0, len = doms.length; i < len; i++) {

            // matched
            if (doms[i] == targetDom) {
                if (typeof doms[i-1] !== "undefined") {
                    prevSibling = doms[i-1];
                }
                if (typeof doms[i+1] !== "undefined") {
                    nextSibling = doms[i+1];
                }
            }
        }

        return {
            next : nextSibling,
            prev : prevSibling
        };
    };

    var AutoComplete = function(targetSelector, userOptions) {

        // options
        userOptions = userOptions || {};

        // internal values
        this.doms = $(targetSelector);

        this.autoCompleteData = userOptions.data || null;
        this.autoCompleteDataSrc = userOptions.dataSrc || "data/autocomplete.json";
        this.autoCompleteSearchTimer = null;
        this.autoCompleteSearchDelay = userOptions.delay || 10;
        this.autoCompleteWrapperClass = "autocomplete-wrapper";
        this.autoCompleteListClass = "autocomplete-list"
        this.autoCompleteOpening = false;
        this.autoCompleteMatchedData = [];

        // start point
        this.init();
    };

    AutoComplete.prototype = {

        init : function() {

            // get data first
            this.getAutocompleteData(function() {

                this.createAutocompleteWrapper();

                this.bindInputSearchEvent();

            });
        },

        createAutocompleteWrapper : function() {
            if ( $("." + this.autoCompleteWrapperClass).length === 0 ) {

                var wrapperDiv = document.createElement("div");
                wrapperDiv.className = this.autoCompleteWrapperClass + " close";

                $("body")[0].appendChild(wrapperDiv);
            }
        },

        createAutocompleteList : function() {

            var fragment = document.createDocumentFragment(),
                matches = this.autoCompleteMatchedData;

            for (var i = 0, len = matches.length; i < len; i++) {

                var match = matches[i],
                    eachListDiv = document.createElement("div");

                eachListDiv.innerHTML = match;
                eachListDiv.className = this.autoCompleteListClass;

                // select the first one
                if (i === 0) {
                    eachListDiv.className += " selected";
                }

                fragment.appendChild(eachListDiv);
            }

            $("." + this.autoCompleteWrapperClass)[0].appendChild(fragment);
        },

        removeAutocompleteList : function() {
            var $wrapper = $("." + this.autoCompleteWrapperClass)[0];

            while($wrapper.firstChild) {
                $wrapper.removeChild($wrapper.firstChild);
            }
        },

        showAutocompleteWrapper : function() {
            if (!this.autoCompleteOpening) {
                $.removeClass("." + this.autoCompleteWrapperClass, "close");
            }
            this.autoCompleteOpening = true;
        },

        hideAutocompleteWrapper : function() {
            if (this.autoCompleteOpening) {
                $.addClass("." + this.autoCompleteWrapperClass, "close");
            }
            this.autoCompleteOpening = false;
        },

        repositionAutocompleteWrapper : function(dom) {

            var VISUAL_OFFSET = 5,
                rect = dom.getBoundingClientRect(),
                $wrapper = $("." + this.autoCompleteWrapperClass)[0];

            $wrapper.style.top = parseInt(rect.top + rect.height + VISUAL_OFFSET, 10) + "px";
            $wrapper.style.left = parseInt(rect.left, 10) + "px";
        },

        getAutocompleteData : function(callback) {

            var that = this;

            // we can pass autoCompleteData from userOptions directly or fetch from a link
            if ( this.autoCompleteData === null ) {
                Getter(this.autoCompleteDataSrc, function(data) {
                    // TODO
                    // check data here
                    var parsedData = JSON.parse(data);
                    that.autoCompleteData = parsedData["autoCompleteData"];

                    callback.call(that);
                });
            }
            else {
                callback.call(that);
            }
        },

        bindInputSearchEvent : function() {

            var that = this;

            for (var i = 0, len = this.doms.length; i < len; i++) {

                var dom = this.doms[i];

                dom.onkeydown = function(e) {

                    if (KeyMap[e.which] === "ENTER") {
                        that.selectMatchedData(dom);
                        that.hideAutocompleteWrapper();
                    }
                    else if (KeyMap[e.which] === "UP") {
                        that.moveAutocompleteSelector("UP");
                        e.preventDefault()
                    }
                    else if (KeyMap[e.which] === "DOWN") {
                        that.moveAutocompleteSelector("DOWN");
                        e.preventDefault()
                    }
                    else if (KeyMap[e.which] === "ESC") {
                        that.hideAutocompleteWrapper();
                        e.preventDefault()
                    }
                    else {

                        window.clearTimeout(that.autoCompleteSearchTimer);
                        that.autoCompleteSearchTimer = window.setTimeout(function() {

                            var userInputValue = that.trimSpaces(dom.value);
                            that.autoCompleteMatchedData = that.searchPossibleMatches(userInputValue);

                            if (that.isMatched()) {

                                // remove them first
                                that.removeAutocompleteList();
                                that.createAutocompleteList();
                                that.repositionAutocompleteWrapper(dom);
                                that.showAutocompleteWrapper();
                            }
                            else {
                                that.hideAutocompleteWrapper();
                            }

                        }, that.autoCompleteSearchDelay);

                    }
                };

                dom.onfocus = function(e) {
                    // because they are still left in the DOM tree
                    if (that.isMatched()) {
                        that.repositionAutocompleteWrapper(dom);
                        that.showAutocompleteWrapper();
                    }
                };

                dom.onblur = function(e) {
                    that.hideAutocompleteWrapper();
                };
            }
        },

        trimSpaces : function(userInputValue) {
            return userInputValue.replace(/^\s\s*/, '').replace(/\s\s*$/,'');
        },

        searchPossibleMatches : function(userInputValue) {

            if (userInputValue.length === 0) {
                return [];
            }

            var matches = [],
                regex = new RegExp("^" + userInputValue, "i");

            for (var i = 0, len = this.autoCompleteData.length; i < len; i++) {

                var currentAcData = this.autoCompleteData[i];

                if (regex.test(currentAcData)) {
                    matches.push(currentAcData);
                }
            }

            return matches;
        },

        moveAutocompleteSelector : function(direction) {

            var siblings = $.siblings("." + this.autoCompleteListClass, "." + this.autoCompleteListClass + ".selected");

            // make sure we have siblings so that we can move selector
            if (siblings.prev && direction === "UP" || siblings.next && direction === "DOWN" ) {

                $.removeClass("." + this.autoCompleteListClass, "selected");

                if (direction === "UP") {
                    $.addClass(siblings.prev, "selected");
                }
                else {
                    $.addClass(siblings.next, "selected");
                }
            }
        },

        selectMatchedData : function(dom) {

            var $selectedListItem = $("." + this.autoCompleteListClass + ".selected")[0],
                matchedData = $selectedListItem.innerHTML;

            dom.value = matchedData;
        },

        isMatched : function() {
            return (this.autoCompleteMatchedData.length > 0);
        }

    };

    window.AutoComplete = AutoComplete;
}());
<|endoftext|>


<commit_before>/**
 * PDP-11 Emulation for JavaScript
 */

/**
 * MemoryUnibus prototype
 *
 * This prototype provides a UNIBUS memory system.
 * @author Takashi Toyoshima <toyoshim@gmail.com>
 */
function MemoryUnibus () {
    this.rk = new DeviceRk(this);
    this.dl = new DeviceDl(this);
    this.ram = new Uint16Array(65536);  // 128KB
    this.logging = false;
    for (var i = 0; i < 65536; i++)
        this.ram[i] = 0;
}

/**
 * Inherit Memory prototype.
 */
MemoryUnibus.prototype = new Memory();
MemoryUnibus.prototype.constructor = MemoryUnibus;

/**
 * Write 8-bit data to addressed memory.
 * @param address memory address to write
 * @param data data to write
 */
MemoryUnibus.prototype.writeChar = function (address, data) {
    if (this.logging)
        Log.getLog().info("WC " + Log.toOct(address, 7) + " <= $" +
                Log.toHex(data , 2));
    var result = this._read(address);
    if (result < 0)
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " write not implemented.");
    if ((address & 1) == 0)
        result = (result & 0xff00) | data;
    else
        result = (result & 0x00ff) | (data << 8);
    if (!this._write(address, result))
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " write not implemented.");
};

/**
 * Read 8-bit data from addressed memory.
 * @param address memory address to read
 * @return read data
 */
MemoryUnibus.prototype.readChar = function (address) {
    var result = -1;
    result = this._read(address);
    if (result >= 0) {
        if ((address & 1) == 0)
            result &= 0xff;
        else
            result = (result >> 8) & 0xff;
    }
    if (this.logging)
        Log.getLog().info("RC " + Log.toOct(address, 7) + " => $" +
                Log.toHex(result , 2));
    if (result < 0)
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " read not implemented.");
    return result;
};

/**
 * Write 16-bit data to addressed memory.
 * @param address memory address to write
 * @param data data to write
 */
MemoryUnibus.prototype.writeShort = function (address, data) {
    if (this.logging)
        Log.getLog().info("WS " + Log.toOct(address, 7) + " <= $" +
                Log.toHex(data , 4));
    if ((address & 1) != 0)
        throw RangeError("Memory alignment error.");
    if (!this._write(address, data))
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " write not implemented.");
};

/**
 * Read 16-bit data from addressed memory.
 * @param address memory address to read
 * @return read data
 */
MemoryUnibus.prototype.readShort = function (address) {
    var result = this._read(address);
    if (this.logging)
        Log.getLog().info("RS " + Log.toOct(address, 7) + " => $" +
                Log.toHex(result , 4));
    if ((address & 1) != 0)
        throw RangeError("Memory alignment error.");
    if (result < 0)
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " read not implemented.");
    return result;
};

/**
 * Internally write 16-bit data to addressed memory.
 * @param address memory address to write
 * @param data data to write
 * @return success
 */
MemoryUnibus.prototype._write = function (address, data) {
    if (address < 0x20000) {
        this.ram[address >> 1] = data;
        return true;
    }
    if (this.rk.write(address, data)) return true;
    if (this.dl.write(address, data)) return true;
    return false;
};

/**
 * Internally read 16-bit data from addressed memory.
 * @param address memory address to read
 * @return read data (-1: failure)
 */
MemoryUnibus.prototype._read = function (address) {
    if (address < 0x20000)
        return this.ram[address >> 1];
    var result = -1;
    if (result < 0)
        result = this.rk.read(address);
    if (result < 0)
        result = this.dl.read(address);
    return result;
};
<commit_msg>Minor refactoring<commit_after>/**
 * PDP-11 Emulation for JavaScript
 */

/**
 * MemoryUnibus prototype
 *
 * This prototype provides a UNIBUS memory system.
 * @author Takashi Toyoshima <toyoshim@gmail.com>
 */
function MemoryUnibus () {
    this.logging = false;
    this.rk = new DeviceRk(this);
    this.dl = new DeviceDl(this);
    this.ram = new Uint16Array(65536);  // 128KB
    for (var i = 0; i < 65536; i++)
        this.ram[i] = 0;
}

/**
 * Inherit Memory prototype.
 */
MemoryUnibus.prototype = new Memory();
MemoryUnibus.prototype.constructor = MemoryUnibus;

/**
 * Write 8-bit data to addressed memory.
 * @param address memory address to write
 * @param data data to write
 */
MemoryUnibus.prototype.writeChar = function (address, data) {
    if (this.logging)
        Log.getLog().info("WC " + Log.toOct(address, 7) + " <= $" +
                Log.toHex(data , 2));
    var result = this._read(address);
    if (result < 0)
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " write not implemented.");
    if ((address & 1) == 0)
        result = (result & 0xff00) | data;
    else
        result = (result & 0x00ff) | (data << 8);
    if (!this._write(address, result))
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " write not implemented.");
};

/**
 * Read 8-bit data from addressed memory.
 * @param address memory address to read
 * @return read data
 */
MemoryUnibus.prototype.readChar = function (address) {
    var result = -1;
    result = this._read(address);
    if (result >= 0) {
        if ((address & 1) == 0)
            result &= 0xff;
        else
            result = (result >> 8) & 0xff;
    }
    if (this.logging)
        Log.getLog().info("RC " + Log.toOct(address, 7) + " => $" +
                Log.toHex(result , 2));
    if (result < 0)
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " read not implemented.");
    return result;
};

/**
 * Write 16-bit data to addressed memory.
 * @param address memory address to write
 * @param data data to write
 */
MemoryUnibus.prototype.writeShort = function (address, data) {
    if (this.logging)
        Log.getLog().info("WS " + Log.toOct(address, 7) + " <= $" +
                Log.toHex(data , 4));
    if ((address & 1) != 0)
        throw RangeError("Memory alignment error.");
    if (!this._write(address, data))
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " write not implemented.");
};

/**
 * Read 16-bit data from addressed memory.
 * @param address memory address to read
 * @return read data
 */
MemoryUnibus.prototype.readShort = function (address) {
    var result = this._read(address);
    if (this.logging)
        Log.getLog().info("RS " + Log.toOct(address, 7) + " => $" +
                Log.toHex(result , 4));
    if ((address & 1) != 0)
        throw RangeError("Memory alignment error.");
    if (result < 0)
        throw RangeError("Memory " + Log.toOct(address, 7) +
                " read not implemented.");
    return result;
};

/**
 * Internally write 16-bit data to addressed memory.
 * @param address memory address to write
 * @param data data to write
 * @return success
 */
MemoryUnibus.prototype._write = function (address, data) {
    if (address < 0x20000) {
        this.ram[address >> 1] = data;
        return true;
    }
    if (this.rk.write(address, data)) return true;
    if (this.dl.write(address, data)) return true;
    return false;
};

/**
 * Internally read 16-bit data from addressed memory.
 * @param address memory address to read
 * @return read data (-1: failure)
 */
MemoryUnibus.prototype._read = function (address) {
    if (address < 0x20000)
        return this.ram[address >> 1];
    var result = -1;
    if (result < 0)
        result = this.rk.read(address);
    if (result < 0)
        result = this.dl.read(address);
    return result;
};
<|endoftext|>


<commit_before>/**
 * Popup menu that is displayed when the user clicks on the bars in the bottom right in the navigation bar.
 * Would be nice to have a balloon triangle dropdown shape like in a comic book dialog.
 *
 * @author Sam Reid
 */
define( function( require ) {
  "use strict";

  var Node = require( 'SCENERY/nodes/Node' );
  var Shape = require( 'KITE/Shape' );
  var Path = require( 'SCENERY/nodes/Path' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Layout = require( 'SCENERY_PHET/Layout' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Button = require( 'SUN/Button' );
  var PanelNode = require( 'SUN/PanelNode' );

  function SimPopupMenu( options ) {
    var simPopupMenu = this;
    Node.call( this );

    var fontSize = '36px';
    var items = [new Text( 'PhET Homepage', {fontSize: fontSize} ),
      new Text( 'Related Sims', {fontSize: fontSize} ),
      new Text( 'About...', {fontSize: fontSize} )];

    //left align the items

    //Compute bounds
    var widestItem = _.max( items, function( item ) {return item.width;} );
    var tallestItem = _.max( items, function( item ) {return item.height;} );
    console.log( widestItem.width );

    var itemHeight = tallestItem.height;

    var verticalSpacing = 10;
    var padding = 10;
    var bubbleWidth = widestItem.width + padding * 2;
    var bubbleHeight = itemHeight * items.length + padding * 2 + verticalSpacing * (items.length - 1);

    console.log( "th", bubbleHeight );

    var bubble = new Rectangle( 0, 0, bubbleWidth, bubbleHeight, 8, 8, {fill: 'white', lineWidth: 1, stroke: 'black'} );

    var tail = new Shape();
    tail.moveTo( bubbleWidth - 20, bubbleHeight - 2 );
    tail.lineToRelative( 0, 20 );
    tail.lineToRelative( -20, -20 );
    tail.close();

    this.addChild( bubble );
    this.addChild( new Path( {shape: tail, fill: 'white'} ) );

    var tailOutline = new Shape();
    tailOutline.moveTo( bubbleWidth - 20, bubbleHeight );
    tailOutline.lineToRelative( 0, 20 - 2 );
    tailOutline.lineToRelative( -18, -18 );
    this.addChild( new Path( {shape: tailOutline, stroke: 'black', lineWidth: 1} ) );

    var y = padding;
    for ( var i = 0; i < items.length; i++ ) {
      var item = items[i];
      item.top = y;
      item.left = padding;
      this.addChild( item );

      if ( i === items.length - 2 ) {
        this.addChild( new Path( {shape: Shape.lineSegment( 8, y + itemHeight + verticalSpacing / 2, bubbleWidth - 8, y + itemHeight + verticalSpacing / 2 ), stroke: 'gray', lineWidth: 1} ) );
      }
      y += itemHeight + verticalSpacing;
    }

    this.addInputListener( { down: function() {
      simPopupMenu.detach();
    } } );
    if ( options ) {
      this.mutate( options );
    }
  }

  inherit( SimPopupMenu, Node );

  return SimPopupMenu;
} );<commit_msg>show highlight when mousing over popup menu items<commit_after>/**
 * Popup menu that is displayed when the user clicks on the bars in the bottom right in the navigation bar.
 * Would be nice to have a balloon triangle dropdown shape like in a comic book dialog.
 *
 * @author Sam Reid
 */
define( function( require ) {
  "use strict";

  var Node = require( 'SCENERY/nodes/Node' );
  var Shape = require( 'KITE/Shape' );
  var Path = require( 'SCENERY/nodes/Path' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Layout = require( 'SCENERY_PHET/Layout' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Button = require( 'SUN/Button' );
  var PanelNode = require( 'SUN/PanelNode' );

  function SimPopupMenu( options ) {
    var simPopupMenu = this;
    Node.call( this );

    var fontSize = '36px';
    var items = [new Text( 'PhET Homepage', {fontSize: fontSize} ),
      new Text( 'Related Sims', {fontSize: fontSize} ),
      new Text( 'About...', {fontSize: fontSize} )];

    //left align the items

    //Compute bounds
    var widestItem = _.max( items, function( item ) {return item.width;} );
    var tallestItem = _.max( items, function( item ) {return item.height;} );
    console.log( widestItem.width );

    var itemHeight = tallestItem.height;

    var verticalSpacing = 10;
    var padding = 10;
    var bubbleWidth = widestItem.width + padding * 2;
    var bubbleHeight = itemHeight * items.length + padding * 2 + verticalSpacing * (items.length - 1);

    console.log( "th", bubbleHeight );

    var bubble = new Rectangle( 0, 0, bubbleWidth, bubbleHeight, 8, 8, {fill: 'white', lineWidth: 1, stroke: 'black'} );

    var tail = new Shape();
    tail.moveTo( bubbleWidth - 20, bubbleHeight - 2 );
    tail.lineToRelative( 0, 20 );
    tail.lineToRelative( -20, -20 );
    tail.close();

    this.addChild( bubble );
    this.addChild( new Path( {shape: tail, fill: 'white'} ) );

    var tailOutline = new Shape();
    tailOutline.moveTo( bubbleWidth - 20, bubbleHeight );
    tailOutline.lineToRelative( 0, 20 - 2 );
    tailOutline.lineToRelative( -18, -18 );
    this.addChild( new Path( {shape: tailOutline, stroke: 'black', lineWidth: 1} ) );

    var y = padding;
    _.each( items, function( item ) {
      item.top = y;
      item.left = padding;
      var highlight = new Rectangle( 3, y - 5, bubbleWidth - 3, itemHeight + 10, 3, 3, {fill: '#a6d2f4', visible: false} );
      simPopupMenu.addChild( highlight );
      simPopupMenu.addChild( item );

      item.cursor = 'pointer';
      item.addInputListener( {enter: function() {
        highlight.visible = true;
      }, exit: function() {
        highlight.visible = false;
      }} );

      if ( item === items[items.length - 2] ) {
        simPopupMenu.addChild( new Path( {shape: Shape.lineSegment( 8, y + itemHeight + verticalSpacing / 2, bubbleWidth - 8, y + itemHeight + verticalSpacing / 2 ), stroke: 'gray', lineWidth: 1} ) );
      }
      y += itemHeight + verticalSpacing;
    } );

    this.addInputListener( { down: function() {
      simPopupMenu.detach();
    } } );
    if ( options ) {
      this.mutate( options );
    }
  }

  inherit( SimPopupMenu, Node );

  return SimPopupMenu;
} );<|endoftext|>