<commit_before>/**
 *
 * SugarCRM Community Edition is a customer relationship management program developed by
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
 *
 * SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
 * Copyright (C) 2011 - 2018 SalesAgility Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along with
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
 * reasonably feasible for technical reasons, the Appropriate Legal Notices must
 * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
 */

if(typeof(SimpleList) == 'undefined'){
	var Dom = YAHOO.util.Dom;
    SimpleList = function(){
        var editImage;
        var deleteImage;
        var ul_list;
        var jstransaction;
        var lastEdit;
        var isIE = isSupportedIE();
        return {
    init: function(editImage, deleteImage) {
        var ul = document.getElementById('ul1', 'drpdwn');
        SimpleList.lastEdit = null; // Bug 14662
        SimpleList.editImage = editImage;
        SimpleList.deleteImage = deleteImage;
        new YAHOO.util.DDTarget("ul1");

        Studio2.scrollZones = {}
        for (var i = 0; Dom.get("ul" + i); i++){
            Studio2.scrollZones["ul" + i] = Studio2.getScrollZones("ul" + i);
        }

        for (i=0;i<SimpleList.ul_list.length;i++){
            if ( typeof SimpleList.ul_list[i] != "number" && SimpleList.ul_list[i] == "" ) {
                SimpleList.ul_list[i] = SUGAR.language.get('ModuleBuilder', 'LBL_BLANK');
            }
            new Studio2.ListDD(SimpleList.ul_list[i], 'drpdwn', false);
        }
        YAHOO.util.Event.on("dropdownaddbtn", "click", this.addToList, 'dropdown_form');
        SimpleList.jstransaction = new JSTransaction();
        SimpleList.jstransaction.register('deleteDropDown', SimpleList.undoDeleteDropDown, SimpleList.undoDeleteDropDown);
        SimpleList.jstransaction.register('changeDropDownValue', SimpleList.undoDropDownChange, SimpleList.redoDropDownChange);

    },
    isValidDropDownKey : function(value){
    	if(value.match(/^[\w\d \.]+$/i) || value == "")
    		return true;

    	return false;
    },
    isBlank : function(value){
    	return value == SUGAR.language.get('ModuleBuilder', 'LBL_BLANK')
    			|| (typeof value != "number" && value == "");
    },
    addToList : function(event, form){
    	var drop_name = document.getElementById('drop_name');
    	var drop_value = document.getElementById('drop_value');
    	//Validate the dropdown key manually
    	removeFromValidate('dropdown_form', 'drop_name');
    	if(!SimpleList.isValidDropDownKey(escape(drop_name.value))) {
			addToValidate('dropdown_form', 'drop_name', 'error', false, SUGAR.language.get("ModuleBuilder", "LBL_JS_VALIDATE_KEY"));
    	}

    	if (!check_form("dropdown_form")) return;

        var ul1=YAHOO.util.Dom.get("ul1");

        var items = ul1.getElementsByTagName("li");
        for (i=0;i<items.length;i=i+1) {
            if((SimpleList.isBlank(items[i].id) && SimpleList.isBlank(escape(drop_name.value))) || items[i].id == escape(drop_name.value)){
                alert(SUGAR.language.get('ModuleBuilder', 'LBL_DROPDOWN_KEY_EXISTS'));
                return;
            }
            if((!SimpleList.isBlank(escape(drop_name.value)) && SimpleList.isBlank(escape(drop_value.value))) || (SimpleList.isBlank(escape(drop_name.value)) && !SimpleList.isBlank(escape(drop_value.value)))){
                alert(SUGAR.language.get('ModuleBuilder', 'LBL_DROPDOWN_BLANK_WARNING'));
                return;
            }
        }

        liObj = document.createElement('li');
        liObj.className = "draggable";
        if(escape(drop_name.value) == '' || !escape(drop_name.value)){
            liObj.id = SUGAR.language.get('ModuleBuilder', 'LBL_BLANK');
        }else{
            liObj.id = escape(drop_name.value);
        }

        var text1 = document.createElement('input');
        text1.type = 'hidden';
        text1.id = 'value_' + liObj.id;
        text1.name = 'value_' + liObj.id;
        text1.value = escape(drop_value.value);

        var html = "

"+liObj.id+"<input id='value_"+liObj.id+"' value=\""+escape(drop_value.value)+"\" type = 'hidden'>";
        if(drop_value.value == ""){
            html += "[" + SUGAR.language.get('ModuleBuilder', 'LBL_BLANK') + "]";
        }else{
            html += "["+YAHOO.lang.escapeHTML(drop_value.value)+"]";
        }
        html += "";
        html += "";
        html += "<input type='text' id='input_"+liObj.id+"' value=\""+YAHOO.lang.escapeHTML(drop_value.value)+"\" onchange='SimpleList.setDropDownValue(\""+liObj.id+"\", unescape(this.value), true)' >";
        html += "";
        html += "	"+SimpleList.editImage+"";
        html += " "+SimpleList.deleteImage+"";
        html += "

";

        liObj.innerHTML = html;
        ul1.appendChild(liObj);
        new Studio2.ListDD(liObj, 'drpdwn', false);
        drop_value.value = "";
        drop_name.value = "";
        drop_name.focus();

        SimpleList.jstransaction.record('deleteDropDown',{'id': liObj.id });

    },

    sortAscending: function ()
    {
        // now sort using a Shellsort - do this rather than by using the inbuilt sort function as we need to sort a complex DOM inplace
        var parent = YAHOO.util.Dom.get("ul1");
        var items = parent.getElementsByTagName("li") ;
        var increment = Math.floor ( items.length / 2 ) ;

        function swapItems(itemA, itemB) {
        	var placeholder = document.createElement ( "li" ) ;
            Dom.insertAfter(placeholder, itemA);
            Dom.insertBefore(itemA, itemB);
            Dom.insertBefore(itemB, placeholder);

            //Cleanup the placeholder element
            parent.removeChild(placeholder);
        }

        while ( increment > 0 )
        {
        	for (var i = increment; i < items.length; i++)
        	{
            	var j = i;
            	var id = items[i].id;
            	var iValue = document.getElementById( 'input_' + id ).value.toLowerCase() ;

            	while ( ( j >= increment ) && ( document.getElementById( 'input_' + items [j-increment].id ).value.toLowerCase() > iValue ) )
            	{
            		// logically, this is what we need to do: items [j] = items [j - increment];
            		// but we're working with the DOM through a NodeList (items) which is readonly, so things aren't that simple
            		// A placeholder will be used to keep track of where in the DOM the swap needs to take place
            		// especially with IE which enforces the prohibition on duplicate Ids, so copying nodes is problematic
            		swapItems(items [j], items [j - increment]);
                	j = j - increment;
            	}
            }

            if (increment == 2)
            	increment = 1;
            else
            	increment = Math.floor (increment / 2.2);
        }
    },
    sortDescending: function ()
    {
        this.sortAscending();
        var reverse = function ( children )
        {
            var parent = children [ 0 ] . parentNode ;
            var start = 0;
            if ( children [ 0 ].id == '-blank-' ) // don't include -blank- element in the sort
                start = 1 ;
            for ( var i = children.length - 1 ; i >= start ; i-- )
            {
                parent.appendChild ( children [ i ] ) ;
            }
        };
        reverse ( YAHOO.util.Dom.get("ul1").getElementsByTagName("li") ) ;
    },
    handleSave:function(){
         var parseList = function(ul, title) {
            var items = ul.getElementsByTagName("li");
            var out = [];
            for (i=0;i<items.length;i=i+1) {
                var name = items[i].id;
                var value = document.getElementById('value_'+name).value;
                out[i] = [ name , unescape(value) ];
            }
            return YAHOO.lang.JSON.stringify(out);
        };
        var ul1=YAHOO.util.Dom.get("ul1");
        var hasDeletedItem = false;
        for(j = 0; j < SimpleList.jstransaction.JSTransactions.length; j++){
            var liEl = new YAHOO.util.Element(SimpleList.jstransaction.JSTransactions[j]['data']['id']);
            if(liEl && liEl.hasClass('deleted'))
            	hasDeletedItem = true;
            	break;
        }
        if(hasDeletedItem) {
        	if(!confirm(SUGAR.language.get('ModuleBuilder', 'LBL_CONFIRM_SAVE_DROPDOWN')))
        		return false;
    	}

        for(j = 0; j < SimpleList.jstransaction.JSTransactions.length; j++){
            if(SimpleList.jstransaction.JSTransactions[j]['transaction'] == 'deleteDropDown'){
                var liEl = new YAHOO.util.Element(SimpleList.jstransaction.JSTransactions[j]['data']['id']);
                if(liEl && liEl.hasClass('deleted'))
                	ul1.removeChild(liEl.get("element"));
            }
        }
        var list = document.getElementById('list_value');

        var out = parseList(ul1, "List 1");
        list.value = out;
        ModuleBuilder.refreshDD_name = document.getElementById('dropdown_name').value;
        if (document.forms.popup_form)
        {
            ModuleBuilder.handleSave("dropdown_form", ModuleBuilder.refreshDropDown);
        }
        else
        {
            ModuleBuilder.handleSave("dropdown_form", ModuleBuilder.refreshGlobalDropDown);
        }
    },
    deleteDropDownValue : function(id, record){
        var field = new YAHOO.util.Element(id);
        if(record){
            SimpleList.jstransaction.record('deleteDropDown',{'id': id });
        }
        if (field.hasClass('deleted'))
            field.removeClass('deleted');
        else
            field.addClass('deleted');
    },
    editDropDownValue : function(id, record){
        //Close any other dropdown edits
        if (SimpleList)
            SimpleList.endCurrentDropDownEdit();
        var dispSpan = document.getElementById('span_'+id);
        var editSpan = document.getElementById('span_edit_'+id);
        dispSpan.style.display = 'none';

        if(SimpleList.isIE){
            editSpan.style.display = 'inline-block';
        }else{
            editSpan.style.display = 'inline';
        }
        var textbox = document.getElementById('input_'+id);
        textbox.focus();
        SimpleList.lastEdit = id;
    },
    endCurrentDropDownEdit : function() {
        if (SimpleList.lastEdit != null)
        {
            var valueLastEdit = unescape(document.getElementById('input_'+SimpleList.lastEdit).value);
            SimpleList.setDropDownValue(SimpleList.lastEdit,valueLastEdit,true);
        }
    },
    setDropDownValue : function(id, val, record){

        if(record){
            SimpleList.jstransaction.record('changeDropDownValue', {'id':id, 'new':val, 'old':document.getElementById('value_'+ id).value});
        }
        let dispSpan = document.getElementById('span_'+id);
        let editSpan = document.getElementById('span_edit_'+id);
        // let textbox = document.getElementById('input_'+id);

        let value = encodeURI(val);

        dispSpan.style.display = 'inline';
        editSpan.style.display = 'none';
        value = decodeURI(value);
        dispSpan.innerHTML = "["+value+"]";
        document.getElementById('value_'+ id).value = value;
        SimpleList.lastEdit = null; // Bug 14662 - clear the last edit point behind us
    },
    undoDeleteDropDown : function(transaction){

        SimpleList.deleteDropDownValue(transaction['id'], false);
    },
    undoDropDownChange : function(transaction){
        SimpleList.setDropDownValue(transaction['id'], transaction['old'], false);
    },
    redoDropDownChange : function(transaction){
        SimpleList.setDropDownValue(transaction['id'], transaction['new'], false);
    },
    undo : function(){
        SimpleList.jstransaction.undo();
    },
    redo : function(){
        SimpleList.jstransaction.redo();
    }
}//return
}();
}<commit_msg>Update SimpleList.js<commit_after>/**
 *
 * SugarCRM Community Edition is a customer relationship management program developed by
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
 *
 * SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
 * Copyright (C) 2011 - 2018 SalesAgility Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along with
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
 * reasonably feasible for technical reasons, the Appropriate Legal Notices must
 * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
 */

if(typeof(SimpleList) == 'undefined'){
	var Dom = YAHOO.util.Dom;
    SimpleList = function(){
        var editImage;
        var deleteImage;
        var ul_list;
        var jstransaction;
        var lastEdit;
        var isIE = isSupportedIE();
        return {
    init: function(editImage, deleteImage) {
        var ul = document.getElementById('ul1', 'drpdwn');
        SimpleList.lastEdit = null; // Bug 14662
        SimpleList.editImage = editImage;
        SimpleList.deleteImage = deleteImage;
        new YAHOO.util.DDTarget("ul1");

        Studio2.scrollZones = {}
        for (var i = 0; Dom.get("ul" + i); i++){
            Studio2.scrollZones["ul" + i] = Studio2.getScrollZones("ul" + i);
        }

        for (i=0;i<SimpleList.ul_list.length;i++){
            if ( typeof SimpleList.ul_list[i] != "number" && SimpleList.ul_list[i] == "" ) {
                SimpleList.ul_list[i] = SUGAR.language.get('ModuleBuilder', 'LBL_BLANK');
            }
            new Studio2.ListDD(SimpleList.ul_list[i], 'drpdwn', false);
        }
        YAHOO.util.Event.on("dropdownaddbtn", "click", this.addToList, 'dropdown_form');
        SimpleList.jstransaction = new JSTransaction();
        SimpleList.jstransaction.register('deleteDropDown', SimpleList.undoDeleteDropDown, SimpleList.undoDeleteDropDown);
        SimpleList.jstransaction.register('changeDropDownValue', SimpleList.undoDropDownChange, SimpleList.redoDropDownChange);

    },
    isValidDropDownKey : function(value){
    	if(value.match(/^[\w\d \.]+$/i) || value == "")
    		return true;

    	return false;
    },
    isBlank : function(value){
    	return value == SUGAR.language.get('ModuleBuilder', 'LBL_BLANK')
    			|| (typeof value != "number" && value == "");
    },
    addToList : function(event, form){
    	var drop_name = document.getElementById('drop_name');
    	var drop_value = document.getElementById('drop_value');
    	//Validate the dropdown key manually
    	removeFromValidate('dropdown_form', 'drop_name');
    	if(!SimpleList.isValidDropDownKey(escape(drop_name.value))) {
			addToValidate('dropdown_form', 'drop_name', 'error', false, SUGAR.language.get("ModuleBuilder", "LBL_JS_VALIDATE_KEY"));
    	}

    	if (!check_form("dropdown_form")) return;

        var ul1=YAHOO.util.Dom.get("ul1");

        var items = ul1.getElementsByTagName("li");
        for (i=0;i<items.length;i=i+1) {
            if((SimpleList.isBlank(items[i].id) && SimpleList.isBlank(escape(drop_name.value))) || items[i].id == escape(drop_name.value)){
                alert(SUGAR.language.get('ModuleBuilder', 'LBL_DROPDOWN_KEY_EXISTS'));
                return;
            }
            if((!SimpleList.isBlank(escape(drop_name.value)) && SimpleList.isBlank(escape(drop_value.value))) || (SimpleList.isBlank(escape(drop_name.value)) && !SimpleList.isBlank(escape(drop_value.value)))){
                alert(SUGAR.language.get('ModuleBuilder', 'LBL_DROPDOWN_BLANK_WARNING'));
                return;
            }
        }

        liObj = document.createElement('li');
        liObj.className = "draggable";
        if(escape(drop_name.value) == '' || !escape(drop_name.value)){
            liObj.id = SUGAR.language.get('ModuleBuilder', 'LBL_BLANK');
        }else{
            liObj.id = escape(drop_name.value);
        }

        var text1 = document.createElement('input');
        text1.type = 'hidden';
        text1.id = 'value_' + liObj.id;
        text1.name = 'value_' + liObj.id;
        text1.value = escape(drop_value.value);

        var html = "

"+liObj.id+"<input id='value_"+liObj.id+"' value=\""+escape(drop_value.value)+"\" type = 'hidden'>";
        if(drop_value.value == ""){
            html += "[" + SUGAR.language.get('ModuleBuilder', 'LBL_BLANK') + "]";
        }else{
            html += "["+YAHOO.lang.escapeHTML(drop_value.value)+"]";
        }
        html += "";
        html += "";
        html += "<input type='text' id='input_"+liObj.id+"' value=\""+YAHOO.lang.escapeHTML(drop_value.value)+"\" onchange='SimpleList.setDropDownValue(\""+liObj.id+"\", unescape(this.value), true)' >";
        html += "";
        html += "	"+SimpleList.editImage+"";
        html += " "+SimpleList.deleteImage+"";
        html += "

";

        liObj.innerHTML = html;
        ul1.appendChild(liObj);
        new Studio2.ListDD(liObj, 'drpdwn', false);
        drop_value.value = "";
        drop_name.value = "";
        drop_name.focus();

        SimpleList.jstransaction.record('deleteDropDown',{'id': liObj.id });

    },

    sortAscending: function ()
    {
        // now sort using a Shellsort - do this rather than by using the inbuilt sort function as we need to sort a complex DOM inplace
        var parent = YAHOO.util.Dom.get("ul1");
        var items = parent.getElementsByTagName("li") ;
        var increment = Math.floor ( items.length / 2 ) ;

        function swapItems(itemA, itemB) {
        	var placeholder = document.createElement ( "li" ) ;
            Dom.insertAfter(placeholder, itemA);
            Dom.insertBefore(itemA, itemB);
            Dom.insertBefore(itemB, placeholder);

            //Cleanup the placeholder element
            parent.removeChild(placeholder);
        }

        while ( increment > 0 )
        {
        	for (var i = increment; i < items.length; i++)
        	{
            	var j = i;
            	var id = items[i].id;
            	var iValue = document.getElementById( 'input_' + id ).value.toLowerCase() ;

            	while ( ( j >= increment ) && ( document.getElementById( 'input_' + items [j-increment].id ).value.toLowerCase() > iValue ) )
            	{
            		// logically, this is what we need to do: items [j] = items [j - increment];
            		// but we're working with the DOM through a NodeList (items) which is readonly, so things aren't that simple
            		// A placeholder will be used to keep track of where in the DOM the swap needs to take place
            		// especially with IE which enforces the prohibition on duplicate Ids, so copying nodes is problematic
            		swapItems(items [j], items [j - increment]);
                	j = j - increment;
            	}
            }

            if (increment == 2)
            	increment = 1;
            else
            	increment = Math.floor (increment / 2.2);
        }
    },
    sortDescending: function ()
    {
        this.sortAscending();
        var reverse = function ( children )
        {
            var parent = children [ 0 ] . parentNode ;
            var start = 0;
            if ( children [ 0 ].id == '-blank-' ) // don't include -blank- element in the sort
                start = 1 ;
            for ( var i = children.length - 1 ; i >= start ; i-- )
            {
                parent.appendChild ( children [ i ] ) ;
            }
        };
        reverse ( YAHOO.util.Dom.get("ul1").getElementsByTagName("li") ) ;
    },
    handleSave:function(){
         var parseList = function(ul, title) {
            var items = ul.getElementsByTagName("li");
            var out = [];
            for (i=0;i<items.length;i=i+1) {
                var name = items[i].id;
                var value = document.getElementById('value_'+name).value;
                out[i] = [ name , unescape(value) ];
            }
            return YAHOO.lang.JSON.stringify(out);
        };
        var ul1=YAHOO.util.Dom.get("ul1");
        var hasDeletedItem = false;
        for(j = 0; j < SimpleList.jstransaction.JSTransactions.length; j++){
            var liEl = new YAHOO.util.Element(SimpleList.jstransaction.JSTransactions[j]['data']['id']);
            if(liEl && liEl.hasClass('deleted'))
            	hasDeletedItem = true;
            	break;
        }
        if(hasDeletedItem) {
        	if(!confirm(SUGAR.language.get('ModuleBuilder', 'LBL_CONFIRM_SAVE_DROPDOWN')))
        		return false;
    	}

        for(j = 0; j < SimpleList.jstransaction.JSTransactions.length; j++){
            if(SimpleList.jstransaction.JSTransactions[j]['transaction'] == 'deleteDropDown'){
                var liEl = new YAHOO.util.Element(SimpleList.jstransaction.JSTransactions[j]['data']['id']);
                if(liEl && liEl.hasClass('deleted'))
                	ul1.removeChild(liEl.get("element"));
            }
        }
        var list = document.getElementById('list_value');

        var out = parseList(ul1, "List 1");
        list.value = out;
        ModuleBuilder.refreshDD_name = document.getElementById('dropdown_name').value;
        if (document.forms.popup_form)
        {
            ModuleBuilder.handleSave("dropdown_form", ModuleBuilder.refreshDropDown);
        }
        else
        {
            ModuleBuilder.handleSave("dropdown_form", ModuleBuilder.refreshGlobalDropDown);
        }
    },
    deleteDropDownValue : function(id, record){
        var field = new YAHOO.util.Element(id);
        if(record){
            SimpleList.jstransaction.record('deleteDropDown',{'id': id });
        }
        if (field.hasClass('deleted'))
            field.removeClass('deleted');
        else
            field.addClass('deleted');
    },
    editDropDownValue : function(id, record){
        //Close any other dropdown edits
        if (SimpleList)
            SimpleList.endCurrentDropDownEdit();
        var dispSpan = document.getElementById('span_'+id);
        var editSpan = document.getElementById('span_edit_'+id);
        dispSpan.style.display = 'none';

        if(SimpleList.isIE){
            editSpan.style.display = 'inline-block';
        }else{
            editSpan.style.display = 'inline';
        }
        var textbox = document.getElementById('input_'+id);
        textbox.focus();
        SimpleList.lastEdit = id;
    },
    endCurrentDropDownEdit : function() {
        if (SimpleList.lastEdit != null)
        {
            var valueLastEdit = unescape(document.getElementById('input_'+SimpleList.lastEdit).value);
            SimpleList.setDropDownValue(SimpleList.lastEdit,valueLastEdit,true);
        }
    },
    setDropDownValue : function(id, val, record){

        if(record){
            SimpleList.jstransaction.record('changeDropDownValue', {'id':id, 'new':val, 'old':document.getElementById('value_'+ id).value});
        }
        let dispSpan = document.getElementById('span_'+id);
        let editSpan = document.getElementById('span_edit_'+id);

        let value = encodeURI(val);

        dispSpan.style.display = 'inline';
        editSpan.style.display = 'none';
        value = decodeURI(value);
        dispSpan.innerHTML = "["+value+"]";
        document.getElementById('value_'+ id).value = value;
        SimpleList.lastEdit = null; // Bug 14662 - clear the last edit point behind us
    },
    undoDeleteDropDown : function(transaction){

        SimpleList.deleteDropDownValue(transaction['id'], false);
    },
    undoDropDownChange : function(transaction){
        SimpleList.setDropDownValue(transaction['id'], transaction['old'], false);
    },
    redoDropDownChange : function(transaction){
        SimpleList.setDropDownValue(transaction['id'], transaction['new'], false);
    },
    undo : function(){
        SimpleList.jstransaction.undo();
    },
    redo : function(){
        SimpleList.jstransaction.redo();
    }
}//return
}();
}
<|endoftext|>


<commit_before>/*
 jQuery AJAX Cross Origin v1.3 (http://www.ajax-cross-origin.com)
 jQuery plugin to bypass Same-origin_policy using Google Apps Script.

 references:
 http://en.wikipedia.org/wiki/Same-origin_policy
 http://www.google.com/script/start/

 (c) 2014, Writen by Erez Ninio. site: www.dealhotelbook.com

 Licensed under the Creative Commons Attribution 3.0 Unported License.
 For details, see http://creativecommons.org/licenses/by/3.0/.
*/

var proxyJsonp="https://script.google.com/macros/s/AKfycbwmqG55tt2d2FcT_WQ3WjCSKmtyFpkOcdprSITn45-4UgVJnzp9/exec";
jQuery.ajaxOrig=jQuery.ajax;jQuery.ajax=function(a,b){function d(a){a=encodeURI(a).replace(/&/g,"%26");return proxyJsonp+"?url="+a+"&callback=?"}var c="object"===typeof a?a:b||{};c.url=c.url||("string"===typeof a?a:"");var c=jQuery.ajaxSetup({},c),e=function(a,c){var b=document.createElement("a");b.href=a;return c.crossOrigin&&"http"==a.substr(0,4).toLowerCase()&&"localhost"!=b.hostname&&"127.0.0.1"!=b.hostname&&b.hostname!=window.location.hostname}(c.url,c);c.proxy&&0<c.proxy.length&&(proxyJsonp=c.proxy,"object"===typeof a?
a.crossDomain=!0:"object"===typeof b&&(b.crossDomain=!0));e&&("object"===typeof a?a.url&&(a.url=d(a.url),a.charset&&(a.url+="&charset="+a.charset),a.dataType="json"):"string"===typeof a&&"object"===typeof b&&(a=d(a),b.charset&&(a+="&charset="+b.charset),b.dataType="json"));return jQuery.ajaxOrig.apply(this,arguments)};jQuery.ajax.prototype=new jQuery.ajaxOrig;jQuery.ajax.prototype.constructor=jQuery.ajax;
<commit_msg>Remove unused lib<commit_after><|endoftext|>


<commit_before>// @flow
import React from 'react'
import {connect} from 'react-redux'
import {
  zoomSelector,
  centerSelector,
  addressesSelector,
  clustersSelector,
  addressesUrlSelector,
} from '../../../selectors'
import {setMapOptions} from '../../../actions/verejneActions'
import './GoogleMap.css'
import {map} from 'lodash'
import ClusterMarker from './ClusterMarker'
import {branch, compose, renderComponent, withHandlers, lifecycle} from 'recompose'
import Loading from '../../Loading'
import GoogleMap from '../../GoogleMap'
import {withDataProviders} from 'data-provider'
import {addressesProvider} from '../../../dataProviders/publiclyDataProviders'
import {withRouter} from 'react-router-dom'
import qs from 'qs'
import {DEFAULT_MAP_CENTER, COUNTRY_ZOOM} from '../../../constants'

import type {MapOptions, Entity} from '../../../state'
import type {MapCluster} from '../../../selectors'
import type {GenericAction} from '../../../types/reduxTypes'
import type {RouterHistory} from 'react-router'

type Props = {
  zoom: number,
  center: [number, number],
  entities: Array<Entity>,
  setMapOptions: (mapOptions: MapOptions) => GenericAction<MapOptions, MapOptions>,
  clusters: Array<MapCluster>,
  onChange: (options: MapOptions) => any,
  history: RouterHistory,
}

// NOTE: there can be multiple points on the map on the same location...
const Map = ({zoom, center, clusters, onChange}: Props) => {
  return (

      <GoogleMap center={center} zoom={zoom} onChange={onChange}>
        {map(clusters, (cluster, i) => (
          <ClusterMarker
            key={i}
            cluster={cluster}
            zoom={zoom}
            lat={cluster.lat}
            lng={cluster.lng}
          />
        ))}
      </GoogleMap>

  )
}

export default compose(
  connect(
    (state) => ({
      zoom: zoomSelector(state),
      center: centerSelector(state),
      addresses: addressesSelector(state),
      clusters: clustersSelector(state),
      addressesUrl: addressesUrlSelector(state),
    }),
    {
      setMapOptions,
    }
  ),
  withDataProviders(({addressesUrl}) => [addressesProvider(addressesUrl)]),
  withRouter,
  withHandlers({
    onChange: (props) => (options) => {
      props.setMapOptions(options)
      props.history.replace(
        `?lat=${options.center.lat.toFixed(6)}&lng=${options.center.lng.toFixed(6)}&zoom=${options.zoom}`
      )
    },
  }),
  lifecycle({
    componentDidMount() {
      const params = qs.parse(this.props.history.location.search.substr(1))
      params.lat = params.lat === undefined ? DEFAULT_MAP_CENTER.lat : params.lat
      params.lng = params.lng === undefined ? DEFAULT_MAP_CENTER.lng : params.lng
      params.zoom = params.zoom === undefined ? COUNTRY_ZOOM : params.zoom
      this.props.setMapOptions({
        center: [Number(params.lat), Number(params.lng)],
        zoom: Number(params.zoom),
      })
    },
  }),
  // display loading only before first fetch
  branch((props) => !props.addresses, renderComponent(Loading))
)(Map)
<commit_msg>PR fixes<commit_after>// @flow
import React from 'react'
import {connect} from 'react-redux'
import {
  zoomSelector,
  centerSelector,
  addressesSelector,
  clustersSelector,
  addressesUrlSelector,
} from '../../../selectors'
import {setMapOptions} from '../../../actions/verejneActions'
import './GoogleMap.css'
import {map} from 'lodash'
import ClusterMarker from './ClusterMarker'
import {branch, compose, renderComponent, withHandlers, lifecycle} from 'recompose'
import Loading from '../../Loading'
import GoogleMap from '../../GoogleMap'
import {withDataProviders} from 'data-provider'
import {addressesProvider} from '../../../dataProviders/publiclyDataProviders'
import {withRouter} from 'react-router-dom'
import qs from 'qs'
import {DEFAULT_MAP_CENTER, COUNTRY_ZOOM} from '../../../constants'

import type {MapOptions, Entity} from '../../../state'
import type {MapCluster} from '../../../selectors'
import type {GenericAction} from '../../../types/reduxTypes'
import type {RouterHistory} from 'react-router'

type Props = {
  zoom: number,
  center: [number, number],
  entities: Array<Entity>,
  setMapOptions: (mapOptions: MapOptions) => GenericAction<MapOptions, MapOptions>,
  clusters: Array<MapCluster>,
  onChange: (options: MapOptions) => any,
  history: RouterHistory,
}

// NOTE: there can be multiple points on the map on the same location...
const Map = ({zoom, center, clusters, onChange}: Props) => {
  return (

      <GoogleMap center={center} zoom={zoom} onChange={onChange}>
        {map(clusters, (cluster, i) => (
          <ClusterMarker
            key={i}
            cluster={cluster}
            zoom={zoom}
            lat={cluster.lat}
            lng={cluster.lng}
          />
        ))}
      </GoogleMap>

  )
}

export default compose(
  connect(
    (state) => ({
      zoom: zoomSelector(state),
      center: centerSelector(state),
      addresses: addressesSelector(state),
      clusters: clustersSelector(state),
      addressesUrl: addressesUrlSelector(state),
    }),
    {
      setMapOptions,
    }
  ),
  withDataProviders(({addressesUrl}) => [addressesProvider(addressesUrl)]),
  withRouter,
  withHandlers({
    onChange: (props) => (options) => {
      props.setMapOptions(options)
      props.history.replace(
        `?lat=${options.center.lat.toFixed(6)}&lng=${options.center.lng.toFixed(6)}&zoom=${options.zoom}`
      )
    },
  }),
  lifecycle({
    componentDidMount() {
      const params = qs.parse(this.props.history.location.search.substr(1))
      params.lat = params.lat || DEFAULT_MAP_CENTER.lat
      params.lng = params.lng || DEFAULT_MAP_CENTER.lng
      params.zoom = params.zoom || COUNTRY_ZOOM
      this.props.setMapOptions({
        center: [Number(params.lat), Number(params.lng)],
        zoom: Number(params.zoom),
      })
    },
  }),
  // display loading only before first fetch
  branch((props) => !props.addresses, renderComponent(Loading))
)(Map)
<|endoftext|>


<commit_before>// FIXME: To all ye that enter here, be weary of Buffers, Arrays and Hex interchanging between the outpoints

var assert = require('assert')
var Address = require('./address')
var BigInteger = require('./bigi')
var Script = require('./script')
var convert = require('./convert')
var crypto = require('./crypto')
var ECKey = require('./eckey').ECKey
var ecdsa = require('./ecdsa')
var Network = require('./network')

var Transaction = function (doc) {
  if (!(this instanceof Transaction)) { return new Transaction(doc) }
  this.version = 1
  this.locktime = 0
  this.ins = []
  this.outs = []
  this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF

  if (doc) {
    if (typeof doc == "string" || Array.isArray(doc)) {
      doc = Transaction.deserialize(doc)
    }

    if (doc.hash) this.hash = doc.hash;
    if (doc.version) this.version = doc.version;
    if (doc.locktime) this.locktime = doc.locktime;
    if (doc.ins && doc.ins.length) {
      doc.ins.forEach(function(input) {
        this.addInput(new TransactionIn(input))
      }, this)
    }

    if (doc.outs && doc.outs.length) {
      doc.outs.forEach(function(output) {
        this.addOutput(new TransactionOut(output))
      }, this)
    }

    this.hash = this.hash || this.getHash()
  }
}

/**
 * Create a new txin.
 *
 * Can be called with any of:
 *
 * - An existing TransactionIn object
 * - A transaction and an index
 * - A transaction hash and an index
 * - A single string argument of the form txhash:index
 *
 * Note that this method does not sign the created input.
 */
Transaction.prototype.addInput = function (tx, outIndex) {
  if (arguments[0] instanceof TransactionIn) {
    this.ins.push(arguments[0])
  }
  else if (arguments[0].length > 65) {
    var args = arguments[0].split(':')
    return this.addInput(args[0], args[1])
  }
  else {
    var hash = typeof tx === "string" ? tx : tx.hash
    hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash

    this.ins.push(new TransactionIn({
      outpoint: {
        hash: hash,
        index: outIndex
      },
      script: new Script(),
      sequence: this.defaultSequence
    }))
  }
}

/**
 * Create a new txout.
 *
 * Can be called with:
 *
 * i) An existing TransactionOut object
 * ii) An address object or an address and a value
 * iii) An address:value string
 * iv) Either ii), iii) with an optional network argument
 *
 * FIXME: This is a bit convoluted
 */
Transaction.prototype.addOutput = function (address, value, network) {
  if (arguments[0] instanceof TransactionOut) {
    this.outs.push(arguments[0])
    return
  }

  if (arguments[0].indexOf(':') >= 0) {
    network = value

    var args = arguments[0].split(':')
    address = args[0]
    value = parseInt(args[1])
  }

  network = network || Network.bitcoin

  if (typeof address === 'string') {
    address = Address.fromBase58Check(address)
  }

  this.outs.push(new TransactionOut({
    value: value,
    script: Script.createOutputScript(address, network)
  }))
}

/**
 * Serialize this transaction.
 *
 * Returns the transaction as a byte array in the standard Bitcoin binary
 * format. This method is byte-perfect, i.e. the resulting byte array can
 * be hashed to get the transaction's standard Bitcoin hash.
 */
Transaction.prototype.serialize = function () {
  var buffer = []
  buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4))
  buffer = buffer.concat(convert.numToVarInt(this.ins.length))

  this.ins.forEach(function(txin) {
    // Why do blockchain.info, blockexplorer.com, sx and just about everybody
    // else use little-endian hashes? No idea...
    buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse())

    buffer = buffer.concat(convert.numToBytes(parseInt(txin.outpoint.index), 4))

    var scriptBytes = txin.script.buffer
    buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
    buffer = buffer.concat(scriptBytes)
    buffer = buffer.concat(txin.sequence)
  })

  buffer = buffer.concat(convert.numToVarInt(this.outs.length))

  this.outs.forEach(function(txout) {
    buffer = buffer.concat(convert.numToBytes(txout.value,8))

    var scriptBytes = txout.script.buffer
    buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
    buffer = buffer.concat(scriptBytes)
  })

  buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime), 4))

  return buffer
}

Transaction.prototype.serializeHex = function() {
  return convert.bytesToHex(this.serialize())
}

//var OP_CODESEPARATOR = 171

var SIGHASH_ALL = 1
var SIGHASH_NONE = 2
var SIGHASH_SINGLE = 3
var SIGHASH_ANYONECANPAY = 80

/**
 * Hash transaction for signing a specific input.
 *
 * Bitcoin uses a different hash for each signed transaction input. This
 * method copies the transaction, makes the necessary changes based on the
 * hashType, serializes and finally hashes the result. This hash can then be
 * used to sign the transaction input in question.
 */
Transaction.prototype.hashTransactionForSignature =
  function (connectedScript, inIndex, hashType)
{
  var txTmp = this.clone()

  // In case concatenating two scripts ends up with two codeseparators,
  // or an extra one at the end, this prevents all those possible
  // incompatibilities.
  /*scriptCode = scriptCode.filter(function (val) {
    return val !== OP_CODESEPARATOR
    });*/

  // Blank out other inputs' signatures
  txTmp.ins.forEach(function(txin) {
    txin.script = new Script()
  })

  txTmp.ins[inIndex].script = connectedScript

  // Blank out some of the outputs
  if ((hashType & 0x1f) == SIGHASH_NONE) {
    txTmp.outs = []

    // Let the others update at will
    txTmp.ins.forEach(function(txin, i) {
      if (i != inIndex) {
        txTmp.ins[i].sequence = 0
      }
    })

  } else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
    // TODO: Implement
  }

  // Blank out other inputs completely, not recommended for open transactions
  if (hashType & SIGHASH_ANYONECANPAY) {
    txTmp.ins = [txTmp.ins[inIndex]]
  }

  var buffer = txTmp.serialize()
  buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4))

  return crypto.hash256(buffer)
}

/**
 * Calculate and return the transaction's hash.
 * Reverses hash since blockchain.info, blockexplorer.com and others
 * use little-endian hashes for some stupid reason
 */
Transaction.prototype.getHash = function ()
{
  var buffer = this.serialize()
  var hash = crypto.hash256(buffer)

  return Array.prototype.slice.call(hash).reverse()
}

Transaction.prototype.clone = function ()
{
  var newTx = new Transaction()
  newTx.version = this.version
  newTx.locktime = this.locktime

  this.ins.forEach(function(txin) {
    newTx.addInput(txin.clone())
  })

  this.outs.forEach(function(txout) {
    newTx.addOutput(txout.clone())
  })

  return newTx
}

Transaction.deserialize = function(buffer) {
  if (typeof buffer == "string") {
    buffer = convert.hexToBytes(buffer)
  }
  var pos = 0
  var readAsInt = function(bytes) {
    if (bytes === 0) return 0;
    pos++;
    return buffer[pos-1] + readAsInt(bytes-1) * 256
  }
  var readVarInt = function() {
    var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read
    var result = convert.varIntToNum(bytes)

    pos += result.bytes.length
    return result.number
  }
  var readBytes = function(bytes) {
    pos += bytes
    return buffer.slice(pos - bytes, pos)
  }
  var readVarString = function() {
    var size = readVarInt()
    return readBytes(size)
  }
  var obj = {
    ins: [],
    outs: []
  }
  obj.version = readAsInt(4)
  var ins = readVarInt()
  var i

  for (i = 0; i < ins; i++) {
    obj.ins.push({
      outpoint: {
        hash: convert.bytesToHex(readBytes(32).reverse()),
        index: readAsInt(4)
      },
      script: new Script(readVarString()),
      sequence: readBytes(4)
    })
  }
  var outs = readVarInt()

  for (i = 0; i < outs; i++) {
    obj.outs.push({
      value: convert.bytesToNum(readBytes(8)),
      script: new Script(readVarString())
    })
  }

  obj.locktime = readAsInt(4)

  return new Transaction(obj)
}

/**
 * Signs a standard output at some index with the given key
 * FIXME: network support is ugly
 */
Transaction.prototype.sign = function(index, key, type, network) {
  assert(key instanceof ECKey)
  type = type || SIGHASH_ALL
  network = network || Network.bitcoin

  var address = key.pub.getAddress(network.pubKeyHash)
  var script = Script.createOutputScript(address, network)
  var hash = this.hashTransactionForSignature(script, index, type)
  var sig = key.sign(hash).concat([type])

  this.ins[index].script = Script.createInputScript(sig, key.pub)
}

// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...]
Transaction.prototype.signWithKeys = function(keys, outputs, type) {
  type = type || SIGHASH_ALL

  var addrdata = keys.map(function(key) {
    assert(key instanceof ECKey)

    return {
      key: key,
      address: key.getAddress().toString()
    }
  })

  var hmap = {}
  outputs.forEach(function(o) {
    hmap[o.output] = o
  })

  for (var i = 0; i < this.ins.length; i++) {
    var outpoint = this.ins[i].outpoint.hash + ':' + this.ins[i].outpoint.index
    var histItem = hmap[outpoint]

    if (!histItem) continue;

    var thisInputAddrdata = addrdata.filter(function(a) {
      return a.address == histItem.address
    })

    if (thisInputAddrdata.length === 0) continue;

    this.sign(i,thisInputAddrdata[0].key)
  }
}

/**
 * Signs a P2SH output at some index with the given key
 */
Transaction.prototype.p2shsign = function(index, script, key, type) {
  script = new Script(script)
  key = new ECKey(key)
  type = type || SIGHASH_ALL
  var hash = this.hashTransactionForSignature(script, index, type),
  sig = key.sign(hash).concat([type])
  return sig
}

Transaction.prototype.multisign = Transaction.prototype.p2shsign

Transaction.prototype.applyMultisigs = function(index, script, sigs/*, type*/) {
  this.ins[index].script = Script.createMultiSigInputScript(sigs, script)
}

Transaction.prototype.validateSig = function(index, script, sig, pub) {
  script = new Script(script)
  var hash = this.hashTransactionForSignature(script,index,1)
  return ecdsa.verify(hash, convert.coerceToBytes(sig),
                      convert.coerceToBytes(pub))
}

Transaction.feePerKb = 20000
Transaction.prototype.estimateFee = function(feePerKb){
  var uncompressedInSize = 180
  var outSize = 34
  var fixedPadding = 34

  if(feePerKb == undefined) feePerKb = Transaction.feePerKb;
  var size = this.ins.length * uncompressedInSize + this.outs.length * outSize + fixedPadding

  return feePerKb * Math.ceil(size / 1000)
}

var TransactionIn = function (data) {
  if (typeof data == "string") {
    this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] }
  } else if (data.outpoint) {
    this.outpoint = data.outpoint
  } else {
    this.outpoint = { hash: data.hash, index: data.index }
  }

  if (data.scriptSig) {
    this.script = Script.fromScriptSig(data.scriptSig)
  } else if (data.script) {
    this.script = data.script
  } else {
    this.script = new Script(data.script)
  }

  this.sequence = data.sequence || this.defaultSequence
}

TransactionIn.prototype.clone = function () {
  return new TransactionIn({
    outpoint: {
      hash: this.outpoint.hash,
      index: this.outpoint.index
    },
    script: this.script.clone(),
    sequence: this.sequence
  })
}

// FIXME: Support for alternate networks
var TransactionOut = function (data) {
  this.script =
      data.script instanceof Script    ? data.script.clone()
    : Array.isArray(data.script)       ? new Script(data.script)
    : typeof data.script == "string"   ? new Script(convert.hexToBytes(data.script))
    : data.scriptPubKey                ? Script.fromScriptSig(data.scriptPubKey)
    : data.address                     ? Script.createOutputScript(data.address)
    : new Script()

  if (this.script.buffer.length > 0) this.address = this.script.getToAddress();

  this.value =
      Array.isArray(data.value)        ? convert.bytesToNum(data.value)
    : "string" == typeof data.value    ? parseInt(data.value)
    : data.value instanceof BigInteger ? parseInt(data.value.toString())
    : data.value
}

TransactionOut.prototype.clone = function() {
  var newTxout = new TransactionOut({
    script: this.script.clone(),
    value: this.value
  })
  return newTxout
}

TransactionOut.prototype.scriptPubKey = function() {
  return convert.bytesToHex(this.script.buffer)
}

module.exports = {
  Transaction: Transaction,
  TransactionIn: TransactionIn,
  TransactionOut: TransactionOut
}
<commit_msg>Removes applyMultisigs and adds setScriptSig<commit_after>// FIXME: To all ye that enter here, be weary of Buffers, Arrays and Hex interchanging between the outpoints

var assert = require('assert')
var Address = require('./address')
var BigInteger = require('./bigi')
var Script = require('./script')
var convert = require('./convert')
var crypto = require('./crypto')
var ECKey = require('./eckey').ECKey
var ecdsa = require('./ecdsa')
var Network = require('./network')

var Transaction = function (doc) {
  if (!(this instanceof Transaction)) { return new Transaction(doc) }
  this.version = 1
  this.locktime = 0
  this.ins = []
  this.outs = []
  this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF

  if (doc) {
    if (typeof doc == "string" || Array.isArray(doc)) {
      doc = Transaction.deserialize(doc)
    }

    if (doc.hash) this.hash = doc.hash;
    if (doc.version) this.version = doc.version;
    if (doc.locktime) this.locktime = doc.locktime;
    if (doc.ins && doc.ins.length) {
      doc.ins.forEach(function(input) {
        this.addInput(new TransactionIn(input))
      }, this)
    }

    if (doc.outs && doc.outs.length) {
      doc.outs.forEach(function(output) {
        this.addOutput(new TransactionOut(output))
      }, this)
    }

    this.hash = this.hash || this.getHash()
  }
}

/**
 * Create a new txin.
 *
 * Can be called with any of:
 *
 * - An existing TransactionIn object
 * - A transaction and an index
 * - A transaction hash and an index
 * - A single string argument of the form txhash:index
 *
 * Note that this method does not sign the created input.
 */
Transaction.prototype.addInput = function (tx, outIndex) {
  if (arguments[0] instanceof TransactionIn) {
    this.ins.push(arguments[0])
  }
  else if (arguments[0].length > 65) {
    var args = arguments[0].split(':')
    return this.addInput(args[0], args[1])
  }
  else {
    var hash = typeof tx === "string" ? tx : tx.hash
    hash = Array.isArray(hash) ? convert.bytesToHex(hash) : hash

    this.ins.push(new TransactionIn({
      outpoint: {
        hash: hash,
        index: outIndex
      },
      script: new Script(),
      sequence: this.defaultSequence
    }))
  }
}

/**
 * Create a new txout.
 *
 * Can be called with:
 *
 * i) An existing TransactionOut object
 * ii) An address object or an address and a value
 * iii) An address:value string
 * iv) Either ii), iii) with an optional network argument
 *
 * FIXME: This is a bit convoluted
 */
Transaction.prototype.addOutput = function (address, value, network) {
  if (arguments[0] instanceof TransactionOut) {
    this.outs.push(arguments[0])
    return
  }

  if (arguments[0].indexOf(':') >= 0) {
    network = value

    var args = arguments[0].split(':')
    address = args[0]
    value = parseInt(args[1])
  }

  network = network || Network.bitcoin

  if (typeof address === 'string') {
    address = Address.fromBase58Check(address)
  }

  this.outs.push(new TransactionOut({
    value: value,
    script: Script.createOutputScript(address, network)
  }))
}

/**
 * Serialize this transaction.
 *
 * Returns the transaction as a byte array in the standard Bitcoin binary
 * format. This method is byte-perfect, i.e. the resulting byte array can
 * be hashed to get the transaction's standard Bitcoin hash.
 */
Transaction.prototype.serialize = function () {
  var buffer = []
  buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4))
  buffer = buffer.concat(convert.numToVarInt(this.ins.length))

  this.ins.forEach(function(txin) {
    // Why do blockchain.info, blockexplorer.com, sx and just about everybody
    // else use little-endian hashes? No idea...
    buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse())

    buffer = buffer.concat(convert.numToBytes(parseInt(txin.outpoint.index), 4))

    var scriptBytes = txin.script.buffer
    buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
    buffer = buffer.concat(scriptBytes)
    buffer = buffer.concat(txin.sequence)
  })

  buffer = buffer.concat(convert.numToVarInt(this.outs.length))

  this.outs.forEach(function(txout) {
    buffer = buffer.concat(convert.numToBytes(txout.value,8))

    var scriptBytes = txout.script.buffer
    buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
    buffer = buffer.concat(scriptBytes)
  })

  buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime), 4))

  return buffer
}

Transaction.prototype.serializeHex = function() {
  return convert.bytesToHex(this.serialize())
}

//var OP_CODESEPARATOR = 171

var SIGHASH_ALL = 1
var SIGHASH_NONE = 2
var SIGHASH_SINGLE = 3
var SIGHASH_ANYONECANPAY = 80

/**
 * Hash transaction for signing a specific input.
 *
 * Bitcoin uses a different hash for each signed transaction input. This
 * method copies the transaction, makes the necessary changes based on the
 * hashType, serializes and finally hashes the result. This hash can then be
 * used to sign the transaction input in question.
 */
Transaction.prototype.hashTransactionForSignature =
  function (connectedScript, inIndex, hashType)
{
  var txTmp = this.clone()

  // In case concatenating two scripts ends up with two codeseparators,
  // or an extra one at the end, this prevents all those possible
  // incompatibilities.
  /*scriptCode = scriptCode.filter(function (val) {
    return val !== OP_CODESEPARATOR
    });*/

  // Blank out other inputs' signatures
  txTmp.ins.forEach(function(txin) {
    txin.script = new Script()
  })

  txTmp.ins[inIndex].script = connectedScript

  // Blank out some of the outputs
  if ((hashType & 0x1f) == SIGHASH_NONE) {
    txTmp.outs = []

    // Let the others update at will
    txTmp.ins.forEach(function(txin, i) {
      if (i != inIndex) {
        txTmp.ins[i].sequence = 0
      }
    })

  } else if ((hashType & 0x1f) == SIGHASH_SINGLE) {
    // TODO: Implement
  }

  // Blank out other inputs completely, not recommended for open transactions
  if (hashType & SIGHASH_ANYONECANPAY) {
    txTmp.ins = [txTmp.ins[inIndex]]
  }

  var buffer = txTmp.serialize()
  buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4))

  return crypto.hash256(buffer)
}

/**
 * Calculate and return the transaction's hash.
 * Reverses hash since blockchain.info, blockexplorer.com and others
 * use little-endian hashes for some stupid reason
 */
Transaction.prototype.getHash = function ()
{
  var buffer = this.serialize()
  var hash = crypto.hash256(buffer)

  return Array.prototype.slice.call(hash).reverse()
}

Transaction.prototype.clone = function ()
{
  var newTx = new Transaction()
  newTx.version = this.version
  newTx.locktime = this.locktime

  this.ins.forEach(function(txin) {
    newTx.addInput(txin.clone())
  })

  this.outs.forEach(function(txout) {
    newTx.addOutput(txout.clone())
  })

  return newTx
}

Transaction.deserialize = function(buffer) {
  if (typeof buffer == "string") {
    buffer = convert.hexToBytes(buffer)
  }
  var pos = 0
  var readAsInt = function(bytes) {
    if (bytes === 0) return 0;
    pos++;
    return buffer[pos-1] + readAsInt(bytes-1) * 256
  }
  var readVarInt = function() {
    var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read
    var result = convert.varIntToNum(bytes)

    pos += result.bytes.length
    return result.number
  }
  var readBytes = function(bytes) {
    pos += bytes
    return buffer.slice(pos - bytes, pos)
  }
  var readVarString = function() {
    var size = readVarInt()
    return readBytes(size)
  }
  var obj = {
    ins: [],
    outs: []
  }
  obj.version = readAsInt(4)
  var ins = readVarInt()
  var i

  for (i = 0; i < ins; i++) {
    obj.ins.push({
      outpoint: {
        hash: convert.bytesToHex(readBytes(32).reverse()),
        index: readAsInt(4)
      },
      script: new Script(readVarString()),
      sequence: readBytes(4)
    })
  }
  var outs = readVarInt()

  for (i = 0; i < outs; i++) {
    obj.outs.push({
      value: convert.bytesToNum(readBytes(8)),
      script: new Script(readVarString())
    })
  }

  obj.locktime = readAsInt(4)

  return new Transaction(obj)
}

/**
 * Signs a standard output at some index with the given key
 * FIXME: network support is ugly
 */
Transaction.prototype.sign = function(index, key, type, network) {
  assert(key instanceof ECKey)
  type = type || SIGHASH_ALL
  network = network || Network.bitcoin

  var address = key.pub.getAddress(network.pubKeyHash)
  var script = Script.createOutputScript(address, network)
  var hash = this.hashTransactionForSignature(script, index, type)
  var sig = key.sign(hash).concat([type])
  var scriptSig = Script.createInputScript(sig, key.pub)

  this.setScriptSig(index, scriptSig)
}

// Takes outputs of the form [{ output: 'txhash:index', address: 'address' },...]
Transaction.prototype.signWithKeys = function(keys, outputs, type) {
  type = type || SIGHASH_ALL

  var addrdata = keys.map(function(key) {
    assert(key instanceof ECKey)

    return {
      key: key,
      address: key.getAddress().toString()
    }
  })

  var hmap = {}
  outputs.forEach(function(o) {
    hmap[o.output] = o
  })

  for (var i = 0; i < this.ins.length; i++) {
    var outpoint = this.ins[i].outpoint.hash + ':' + this.ins[i].outpoint.index
    var histItem = hmap[outpoint]

    if (!histItem) continue;

    var thisInputAddrdata = addrdata.filter(function(a) {
      return a.address == histItem.address
    })

    if (thisInputAddrdata.length === 0) continue;

    this.sign(i,thisInputAddrdata[0].key)
  }
}

/**
 * Signs a P2SH output at some index with the given key
 */
Transaction.prototype.p2shsign = function(index, script, key, type) {
  script = new Script(script)
  key = new ECKey(key)
  type = type || SIGHASH_ALL
  var hash = this.hashTransactionForSignature(script, index, type),
  sig = key.sign(hash).concat([type])
  return sig
}

Transaction.prototype.setScriptSig = function(index, script) {
  this.ins[index].script = script
}

Transaction.prototype.validateSig = function(index, script, sig, pub) {
  script = new Script(script)
  var hash = this.hashTransactionForSignature(script,index,1)
  return ecdsa.verify(hash, convert.coerceToBytes(sig),
                      convert.coerceToBytes(pub))
}

Transaction.feePerKb = 20000
Transaction.prototype.estimateFee = function(feePerKb){
  var uncompressedInSize = 180
  var outSize = 34
  var fixedPadding = 34

  if(feePerKb == undefined) feePerKb = Transaction.feePerKb;
  var size = this.ins.length * uncompressedInSize + this.outs.length * outSize + fixedPadding

  return feePerKb * Math.ceil(size / 1000)
}

var TransactionIn = function (data) {
  if (typeof data == "string") {
    this.outpoint = { hash: data.split(':')[0], index: data.split(':')[1] }
  } else if (data.outpoint) {
    this.outpoint = data.outpoint
  } else {
    this.outpoint = { hash: data.hash, index: data.index }
  }

  if (data.scriptSig) {
    this.script = Script.fromScriptSig(data.scriptSig)
  } else if (data.script) {
    this.script = data.script
  } else {
    this.script = new Script(data.script)
  }

  this.sequence = data.sequence || this.defaultSequence
}

TransactionIn.prototype.clone = function () {
  return new TransactionIn({
    outpoint: {
      hash: this.outpoint.hash,
      index: this.outpoint.index
    },
    script: this.script.clone(),
    sequence: this.sequence
  })
}

// FIXME: Support for alternate networks
var TransactionOut = function (data) {
  this.script =
      data.script instanceof Script    ? data.script.clone()
    : Array.isArray(data.script)       ? new Script(data.script)
    : typeof data.script == "string"   ? new Script(convert.hexToBytes(data.script))
    : data.scriptPubKey                ? Script.fromScriptSig(data.scriptPubKey)
    : data.address                     ? Script.createOutputScript(data.address)
    : new Script()

  if (this.script.buffer.length > 0) this.address = this.script.getToAddress();

  this.value =
      Array.isArray(data.value)        ? convert.bytesToNum(data.value)
    : "string" == typeof data.value    ? parseInt(data.value)
    : data.value instanceof BigInteger ? parseInt(data.value.toString())
    : data.value
}

TransactionOut.prototype.clone = function() {
  var newTxout = new TransactionOut({
    script: this.script.clone(),
    value: this.value
  })
  return newTxout
}

TransactionOut.prototype.scriptPubKey = function() {
  return convert.bytesToHex(this.script.buffer)
}

module.exports = {
  Transaction: Transaction,
  TransactionIn: TransactionIn,
  TransactionOut: TransactionOut
}
<|endoftext|>


<commit_before>import React from "react";
import { connect } from "react-redux";
import { push } from "react-router-redux";
import { withRouter } from "react-router-dom";
import { Row, Col, Modal, Alert, ButtonToolbar } from "react-bootstrap";

import { Icon, Flex, FlexItem, Input, Button } from "../../base";
import { createVirus } from "../actions";

const getInitialState = () => ({
    name: "",
    abbreviation: ""
});

class CreateVirus extends React.Component {

    constructor (props) {
        super(props);
        this.state = getInitialState();
        this.state.show = false;
    }

    handleChange = (e) => {
        const { name, value } = e.target;
        this.setState({
            [name]: value
        });
    };

    handleHide = () => {
        this.props.onHide(this.props);
    };

    handleModalExited = () => {
        this.setState(getInitialState());
    };

    handleSubmit = (e) => {
        e.preventDefault();

        if (!this.state.name || !this.state.abbreviation) {
            this.setState({show: true});
        } else {
            this.setState({show: false});
            this.props.onSubmit(this.state.name, this.state.abbreviation);
        }
    };

    render () {

        let alert;

        if (this.props.error) {
            alert = (
                <Alert bsStyle="danger">
                    <Flex>
                        <FlexItem grow={0} shrink={0}>
                            <Icon name="warning" />
                        </FlexItem>
                        <FlexItem grow={1} shrink={0} pad>
                            {this.props.error}
                        </FlexItem>
                    </Flex>
                </Alert>
            );
        }

        let errorMessage = this.state.show ? "Required Field" : "";

        return (
            <Modal show={this.props.show} onHide={this.handleHide} onExited={this.handleModalExited}>
                <Modal.Header onHide={this.props.onHide} closeButton>
                    Create Virus
                </Modal.Header>

                <form onSubmit={this.handleSubmit}>
                    <Modal.Body>
                        <Row>
                            <Col md={9}>
                                <Input
                                    label="Name"
                                    name="name"
                                    value={this.state.name}
                                    onChange={this.handleChange}
                                    error={errorMessage}
                                />
                            </Col>
                            <Col md={3}>
                                <Input
                                    label="Abbreviation"
                                    name="abbreviation"
                                    value={this.state.abbreviation}
                                    onChange={this.handleChange}
                                    error={errorMessage}
                                />
                            </Col>
                        </Row>

                        {alert}
                    </Modal.Body>

                    <Modal.Footer>
                        <ButtonToolbar className="pull-right">
                            <Button icon="floppy" type="submit" bsStyle="primary">
                                Save
                            </Button>
                        </ButtonToolbar>
                    </Modal.Footer>
                </form>
            </Modal>
        );
    }
}

const mapStateToProps = state => ({
    show: !!state.router.location.state && state.router.location.state.createVirus,
    error: state.viruses.createError,
    pending: state.viruses.createPending
});

const mapDispatchToProps = dispatch => ({

    onSubmit: (name, abbreviation) => {
        dispatch(createVirus(name, abbreviation));
    },

    onHide: ({ location }) => {
        dispatch(push({...location, state: {createVirus: false}}));
    }

});

const CreateVirusContainer = withRouter(connect(mapStateToProps, mapDispatchToProps)(CreateVirus));

export default CreateVirusContainer;

<commit_msg>Fix virus creation only name required<commit_after>import React from "react";
import { connect } from "react-redux";
import { push } from "react-router-redux";
import { withRouter } from "react-router-dom";
import { Row, Col, Modal, Alert, ButtonToolbar } from "react-bootstrap";

import { Icon, Flex, FlexItem, Input, Button } from "../../base";
import { createVirus } from "../actions";

const getInitialState = () => ({
    name: "",
    abbreviation: ""
});

class CreateVirus extends React.Component {

    constructor (props) {
        super(props);
        this.state = getInitialState();
        this.state.show = false;
    }

    handleChange = (e) => {
        const { name, value } = e.target;
        this.setState({
            [name]: value
        });
    };

    handleHide = () => {
        this.props.onHide(this.props);
    };

    handleModalExited = () => {
        this.setState(getInitialState());
    };

    handleSubmit = (e) => {
        e.preventDefault();

        if (!this.state.name) {
            this.setState({show: true});
        } else {
            this.setState({show: false});
            this.props.onSubmit(this.state.name, this.state.abbreviation);
        }
    };

    render () {

        let alert;

        if (this.props.error) {
            alert = (
                <Alert bsStyle="danger">
                    <Flex>
                        <FlexItem grow={0} shrink={0}>
                            <Icon name="warning" />
                        </FlexItem>
                        <FlexItem grow={1} shrink={0} pad>
                            {this.props.error}
                        </FlexItem>
                    </Flex>
                </Alert>
            );
        }

        let errorMessage = this.state.show ? "Required Field" : "";

        return (
            <Modal show={this.props.show} onHide={this.handleHide} onExited={this.handleModalExited}>
                <Modal.Header onHide={this.props.onHide} closeButton>
                    Create Virus
                </Modal.Header>

                <form onSubmit={this.handleSubmit}>
                    <Modal.Body>
                        <Row>
                            <Col md={9}>
                                <Input
                                    label="Name"
                                    name="name"
                                    value={this.state.name}
                                    onChange={this.handleChange}
                                    error={errorMessage}
                                />
                            </Col>
                            <Col md={3}>
                                <Input
                                    label="Abbreviation"
                                    name="abbreviation"
                                    value={this.state.abbreviation}
                                    onChange={this.handleChange}
                                />
                            </Col>
                        </Row>

                        {alert}
                    </Modal.Body>

                    <Modal.Footer>
                        <ButtonToolbar className="pull-right">
                            <Button icon="floppy" type="submit" bsStyle="primary">
                                Save
                            </Button>
                        </ButtonToolbar>
                    </Modal.Footer>
                </form>
            </Modal>
        );
    }
}

const mapStateToProps = state => ({
    show: !!state.router.location.state && state.router.location.state.createVirus,
    error: state.viruses.createError,
    pending: state.viruses.createPending
});

const mapDispatchToProps = dispatch => ({

    onSubmit: (name, abbreviation) => {
        dispatch(createVirus(name, abbreviation));
    },

    onHide: ({ location }) => {
        dispatch(push({...location, state: {createVirus: false}}));
    }

});

const CreateVirusContainer = withRouter(connect(mapStateToProps, mapDispatchToProps)(CreateVirus));

export default CreateVirusContainer;

<|endoftext|>


<commit_before>Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

// Based on:
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/console/hudservice/HUDService.jsm#3240
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript

const Cc = Components.classes;
const Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/NetUtil.jsm");

function dumpln(s) { dump(s + "\n"); }

function DOMFuzzHelper() {}

function quitFromContent() { dumpln("Got goQuitApplication from page"); goQuitApplication(); }
quitFromContent.__exposedProps__ = {"toString": "r"};

DOMFuzzHelper.prototype = {
  classDescription: "DOM fuzz helper",
  classID:          Components.ID("{59a52458-13e0-4d90-9d85-a637344f29a1}"),
  contractID:       "@squarefree.com/dom-fuzz-helper;1",

  QueryInterface:   XPCOMUtils.generateQI([Components.interfaces.nsIObserver]),
  _xpcom_categories: [{category: "profile-after-change", service: true }],

  observe: function(aSubject, aTopic, aData)
  {
    if (aTopic == "profile-after-change") {
      this.init();
    } else if (aTopic == "content-document-global-created") {
      var w = aSubject.wrappedJSObject;

      if (w) {
        w.goQuitApplication = quitFromContent;
        w.fuzzPrivRunSoon = runSoon;
        w.fuzzPrivEnableAccessibility = enableAccessibility;
        w.fuzzPrivGC = function() { Components.utils.forceGC(); };
        w.fuzzPrivMP = sendMemoryPressureNotification;
        w.fuzzPrivCC = cycleCollect(aSubject);
        // w.fuzzPrivZoom = setZoomLevel(aSubject); // bug 576927
        w.fuzzPrivPrintToFile = printToFile(aSubject);
        w.fuzzPrivQuitWithLeakCheck = quitWithLeakCheck;
      } else {
        // I don't understand why this happens.  Some chrome windows sneak in here?
      }
    } else if (aTopic == "xpcom-shutdown") {
      this.uninit();
    }
  },

  init: function()
  {
    var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
    obs.addObserver(this, "xpcom-shutdown", false);
    obs.addObserver(this, "content-document-global-created", false);
  },

  uninit: function()
  {
    var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
    obs.removeObserver(this, "content-document-global-created");
  },
};

const NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFuzzHelper]);

/*****************************
 * MISC PRIVILEGED FUNCTIONS *
 *****************************/

function runSoon(f)
{
  var tm = Components.classes["@mozilla.org/thread-manager;1"]
             .getService(Components.interfaces.nsIThreadManager);

  tm.mainThread.dispatch({
    run: function() {
      f();
    }
  }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}

function enableAccessibility()
{
  try {
    Components.classes["@mozilla.org/accessibilityService;1"]
      .getService(Components.interfaces.nsIAccessibleRetrieval);
    dump("Enabled accessibility!\n");
  } catch(e) {
    dump("Couldn't enable accessibility: " + e + "\n");
  }
}

function sendMemoryPressureNotification()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
           .getService(Components.interfaces.nsIObserverService);
  os.notifyObservers(null, "memory-pressure", "heap-minimize");
}

function cycleCollect(window)
{
  return function cycleCollectInner() {
    window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
          .getInterface(Components.interfaces.nsIDOMWindowUtils)
          .garbageCollect();
  }
}

function setZoomLevel(window)
{
  return function setZoomLevelInner(textOrFull, factor) {
    var viewer = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIWebNavigation)
                       .QueryInterface(Components.interfaces.nsIDocShell)
                       .contentViewer
                       .QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);

    if (textOrFull == "text")
      viewer.textZoom = +factor;
    else if (textOrFull == "full")
      viewer.fullZoom = +factor;
  }
}

function printToFile(window)
{
  // Linux: tested, works for PDF and PS, oddly asynchronous.
  // Mac: tested, printToFile is ignored and it goes to a printer!
  // Windows: untested.
  var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
                             .getService(Components.interfaces.nsIXULRuntime);
  if (xulRuntime.OS != "Linux") return function() { };

  var fired = false;

  return function printToFileInner(showHeaders, showBGColor, showBGImages, ps) {
    runSoon(function() {
        // Don't print more than once, it gets messy fast.
        if (fired) { return false; }
        fired = true;

        // Based on https://addons.mozilla.org/en-US/firefox/addon/5971/ by pav and bho

        var webBrowserPrint = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
        .getInterface(Components.interfaces.nsIWebBrowserPrint);

        var nsIPrintSettings = Components.interfaces.nsIPrintSettings;

        var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
        .getService(Components.interfaces.nsIPrintSettingsService);

        var printSettings = PSSVC.newPrintSettings;

        var file = Components.classes["@mozilla.org/file/directory_service;1"].
                              getService(Components.interfaces.nsIProperties).
                              get("ProfD", Components.interfaces.nsIFile);
        file.append(ps ? "a.ps" : "a.pdf");
        dumpln("Printing to: " + file.path);

        printSettings.printToFile = true;
        printSettings.toFileName  = file.path;
        printSettings.printSilent = true;
        printSettings.outputFormat = ps ? nsIPrintSettings.kOutputFormatPS : nsIPrintSettings.kOutputFormatPDF;
        printSettings.printBGColors   = !!showBGColor;
        printSettings.printBGImages   = !!showBGImages;
        if (!showHeaders) {
            printSettings.footerStrCenter = '';
            printSettings.footerStrLeft   = '';
            printSettings.footerStrRight  = '';
            printSettings.headerStrCenter = '';
            printSettings.headerStrLeft   = '';
            printSettings.headerStrRight  = '';
        }

        webBrowserPrint.print(printSettings, null);
    });
  }
}

/************************
 * QUIT WITH LEAK CHECK *
 ************************/

var quitting = false;

function quitWithLeakCheck()
{
  if (quitting)
    return;
  quitting = true;

  runSoon(a);
  function a() { dumpln("QA"); closeAllWindows(); runOnTimer(b); dumpln("QAA"); }
  function b() { dumpln("QB"); mpUntilDone(c); }
  function c() { dumpln("QC"); bloatStats(d); }
  function d(objectCounts) {
    dumpln("QD");

    dumpln("Windows: " + objectCounts["nsGlobalWindow"]);
    dumpln("Documents: " + objectCounts["nsDocument"]);

    //if (objectCounts["nsGlobalWindow"] > 4) { dumpln("OMGLEAK"); }
    //if (objectCounts["nsDocument"] > 4) { dumpln("OMGLEAK"); }
    runSoon(e);
  }
  function e() { dumpln("QE"); goQuitApplication(); }
}

var timerDeathGrip;
function runOnTimer(f)
{
    timerDeathGrip = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    timerDeathGrip.initWithCallback({notify: function(){ timerDeathGrip=null; f(); }}, 2000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

function closeAllWindows()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);
  var enumerator = ww.getWindowEnumerator();

  var windowsToClose = [];

  while (enumerator.hasMoreElements()) {
    windowsToClose.push(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
  }

  // if not mac...
  ww.openWindow(null, "about:blank", null, "width=200,height=200", null);

  for (var i = 0; i < windowsToClose.length; ++i) {
    windowsToClose[i].close();
  }

  dumpln("1");
}

function mpUntilDone(callback)
{
  function mpUntilDoneInner()
  {
    dumpln("MP " + j);
    sendMemoryPressureNotification();

    ++j;
    if (j > 9)
      runSoon(callback);
    else
      runSoon(mpUntilDoneInner);
  }

  var j = 0;
  mpUntilDoneInner();
}

/*
     |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|
                                              Per-Inst   Leaked    Total      Rem      Mean       StdDev     Total      Rem      Mean       StdDev

*/
// Grab the class name and the number of remaining objects.
var bloatRex = /\s*\d+\s+(\S+)\s+\d+\s+\d+\s+\d+\s+(\d+)\s+.*/;
const SET_QUOTA = false;
const USE_QUOTA = false;

function bloatStats(callback)
{
  var objectCounts = {};

  try {
    //d.d.d;
    NetUtil.asyncFetch("about:bloat", fetched);
  } catch(e) {
    dumpln("Can't open about:bloat -- maybe you forgot to use XPCOM_MEM_LEAK_LOG");
    callback(objectCounts);
  }

  function fetched(aInputStream, aResult)
  {
    var r = NetUtil.readInputStreamToString(aInputStream, aInputStream.available());
    var lines = r.split("\n");
    for (var i = 0; i < lines.length; ++i)
    {
      var a = bloatRex.exec(lines[i]);
      if (a) {
        if (SET_QUOTA) {
          dumpln("'" + a[1] + "': " + a[2] + ",");
        } else if (USE_QUOTA) {
          quotaA = QUOTA[a[1]] || 0;
          if (a[2] > quotaA) { dumpln("Leak? Too many " + a[1] + " (" + a[2] + " > " + quotaA + ")"); }
        }
        objectCounts[a[1]] = a[2];
      }
    }
    runSoon(callCallback);
  }

  function callCallback()
  {
    callback(objectCounts)
  }
}

/********
 * QUIT *
 ********/

// From quit.js, which Bob Clary extracted from mozilla/toolkit/content

function canQuitApplication()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
    .getService(Components.interfaces.nsIObserverService);
  if (!os)
  {
    return true;
  }

  try
 {
    var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
      .createInstance(Components.interfaces.nsISupportsPRBool);
    os.notifyObservers(cancelQuit, "quit-application-requested", null);

    // Something aborted the quit process.
    if (cancelQuit.data)
    {
      return false;
    }
  }
  catch (ex)
  {
  }
  os.notifyObservers(null, "quit-application-granted", null);
  return true;
}

function goQuitApplication()
{
  dumpln("goQuitApplication (js component)");

  if (!canQuitApplication())
  {
    return false;
  }

  var kAppStartup = '@mozilla.org/toolkit/app-startup;1';
  var kAppShell   = '@mozilla.org/appshell/appShellService;1';
  var   appService;
  var   forceQuit;

  if (kAppStartup in Components.classes)
  {
    appService = Components.classes[kAppStartup].
      getService(Components.interfaces.nsIAppStartup);
    forceQuit  = Components.interfaces.nsIAppStartup.eForceQuit;

  }
  else if (kAppShell in Components.classes)
  {
    appService = Components.classes[kAppShell].
      getService(Components.interfaces.nsIAppShellService);
    forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
  }
  else
  {
    throw 'goQuitApplication: no AppStartup/appShell';
  }

  var windowManager = Components.
    classes['@mozilla.org/appshell/window-mediator;1'].getService();

  var windowManagerInterface = windowManager.
    QueryInterface(Components.interfaces.nsIWindowMediator);

  var enumerator = windowManagerInterface.getEnumerator(null);

  while (enumerator.hasMoreElements())
  {
    var domWindow = enumerator.getNext();
    if (("tryToClose" in domWindow) && !domWindow.tryToClose())
    {
      return false;
    }
    domWindow.close();
  }

  try
  {
    appService.quit(forceQuit);
  }
  catch(ex)
  {
    throw('goQuitApplication: ' + ex);
  }

  return true;
}

<commit_msg>Use runSoon again to avoid fake leaks<commit_after>Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

// Based on:
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/console/hudservice/HUDService.jsm#3240
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript

const Cc = Components.classes;
const Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/NetUtil.jsm");

function dumpln(s) { dump(s + "\n"); }

function DOMFuzzHelper() {}

// Use runSoon to avoid false-positive leaks due to content JS on the stack (?)
function quitFromContent() { dumpln("Got goQuitApplication from page"); runSoon(goQuitApplication); }
quitFromContent.__exposedProps__ = {"toString": "r"};

DOMFuzzHelper.prototype = {
  classDescription: "DOM fuzz helper",
  classID:          Components.ID("{59a52458-13e0-4d90-9d85-a637344f29a1}"),
  contractID:       "@squarefree.com/dom-fuzz-helper;1",

  QueryInterface:   XPCOMUtils.generateQI([Components.interfaces.nsIObserver]),
  _xpcom_categories: [{category: "profile-after-change", service: true }],

  observe: function(aSubject, aTopic, aData)
  {
    if (aTopic == "profile-after-change") {
      this.init();
    } else if (aTopic == "content-document-global-created") {
      var w = aSubject.wrappedJSObject;

      if (w) {
        w.goQuitApplication = quitFromContent;
        w.fuzzPrivRunSoon = runSoon;
        w.fuzzPrivEnableAccessibility = enableAccessibility;
        w.fuzzPrivGC = function() { Components.utils.forceGC(); };
        w.fuzzPrivMP = sendMemoryPressureNotification;
        w.fuzzPrivCC = cycleCollect(aSubject);
        // w.fuzzPrivZoom = setZoomLevel(aSubject); // bug 576927
        w.fuzzPrivPrintToFile = printToFile(aSubject);
        w.fuzzPrivQuitWithLeakCheck = quitWithLeakCheck;
      } else {
        // I don't understand why this happens.  Some chrome windows sneak in here?
      }
    } else if (aTopic == "xpcom-shutdown") {
      this.uninit();
    }
  },

  init: function()
  {
    var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
    obs.addObserver(this, "xpcom-shutdown", false);
    obs.addObserver(this, "content-document-global-created", false);
  },

  uninit: function()
  {
    var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
    obs.removeObserver(this, "content-document-global-created");
  },
};

const NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFuzzHelper]);

/*****************************
 * MISC PRIVILEGED FUNCTIONS *
 *****************************/

function runSoon(f)
{
  var tm = Components.classes["@mozilla.org/thread-manager;1"]
             .getService(Components.interfaces.nsIThreadManager);

  tm.mainThread.dispatch({
    run: function() {
      f();
    }
  }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}

function enableAccessibility()
{
  try {
    Components.classes["@mozilla.org/accessibilityService;1"]
      .getService(Components.interfaces.nsIAccessibleRetrieval);
    dump("Enabled accessibility!\n");
  } catch(e) {
    dump("Couldn't enable accessibility: " + e + "\n");
  }
}

function sendMemoryPressureNotification()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
           .getService(Components.interfaces.nsIObserverService);
  os.notifyObservers(null, "memory-pressure", "heap-minimize");
}

function cycleCollect(window)
{
  return function cycleCollectInner() {
    window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
          .getInterface(Components.interfaces.nsIDOMWindowUtils)
          .garbageCollect();
  }
}

function setZoomLevel(window)
{
  return function setZoomLevelInner(textOrFull, factor) {
    var viewer = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIWebNavigation)
                       .QueryInterface(Components.interfaces.nsIDocShell)
                       .contentViewer
                       .QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);

    if (textOrFull == "text")
      viewer.textZoom = +factor;
    else if (textOrFull == "full")
      viewer.fullZoom = +factor;
  }
}

function printToFile(window)
{
  // Linux: tested, works for PDF and PS, oddly asynchronous.
  // Mac: tested, printToFile is ignored and it goes to a printer!
  // Windows: untested.
  var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
                             .getService(Components.interfaces.nsIXULRuntime);
  if (xulRuntime.OS != "Linux") return function() { };

  var fired = false;

  return function printToFileInner(showHeaders, showBGColor, showBGImages, ps) {
    runSoon(function() {
        // Don't print more than once, it gets messy fast.
        if (fired) { return false; }
        fired = true;

        // Based on https://addons.mozilla.org/en-US/firefox/addon/5971/ by pav and bho

        var webBrowserPrint = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
        .getInterface(Components.interfaces.nsIWebBrowserPrint);

        var nsIPrintSettings = Components.interfaces.nsIPrintSettings;

        var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
        .getService(Components.interfaces.nsIPrintSettingsService);

        var printSettings = PSSVC.newPrintSettings;

        var file = Components.classes["@mozilla.org/file/directory_service;1"].
                              getService(Components.interfaces.nsIProperties).
                              get("ProfD", Components.interfaces.nsIFile);
        file.append(ps ? "a.ps" : "a.pdf");
        dumpln("Printing to: " + file.path);

        printSettings.printToFile = true;
        printSettings.toFileName  = file.path;
        printSettings.printSilent = true;
        printSettings.outputFormat = ps ? nsIPrintSettings.kOutputFormatPS : nsIPrintSettings.kOutputFormatPDF;
        printSettings.printBGColors   = !!showBGColor;
        printSettings.printBGImages   = !!showBGImages;
        if (!showHeaders) {
            printSettings.footerStrCenter = '';
            printSettings.footerStrLeft   = '';
            printSettings.footerStrRight  = '';
            printSettings.headerStrCenter = '';
            printSettings.headerStrLeft   = '';
            printSettings.headerStrRight  = '';
        }

        webBrowserPrint.print(printSettings, null);
    });
  }
}

/************************
 * QUIT WITH LEAK CHECK *
 ************************/

var quitting = false;

function quitWithLeakCheck()
{
  if (quitting)
    return;
  quitting = true;

  runSoon(a);
  function a() { dumpln("QA"); closeAllWindows(); runOnTimer(b); dumpln("QAA"); }
  function b() { dumpln("QB"); mpUntilDone(c); }
  function c() { dumpln("QC"); bloatStats(d); }
  function d(objectCounts) {
    dumpln("QD");

    dumpln("Windows: " + objectCounts["nsGlobalWindow"]);
    dumpln("Documents: " + objectCounts["nsDocument"]);

    //if (objectCounts["nsGlobalWindow"] > 4) { dumpln("OMGLEAK"); }
    //if (objectCounts["nsDocument"] > 4) { dumpln("OMGLEAK"); }
    runSoon(e);
  }
  function e() { dumpln("QE"); goQuitApplication(); }
}

var timerDeathGrip;
function runOnTimer(f)
{
    timerDeathGrip = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    timerDeathGrip.initWithCallback({notify: function(){ timerDeathGrip=null; f(); }}, 2000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

function closeAllWindows()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);
  var enumerator = ww.getWindowEnumerator();

  var windowsToClose = [];

  while (enumerator.hasMoreElements()) {
    windowsToClose.push(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
  }

  // if not mac...
  ww.openWindow(null, "about:blank", null, "width=200,height=200", null);

  for (var i = 0; i < windowsToClose.length; ++i) {
    windowsToClose[i].close();
  }

  dumpln("1");
}

function mpUntilDone(callback)
{
  function mpUntilDoneInner()
  {
    dumpln("MP " + j);
    sendMemoryPressureNotification();

    ++j;
    if (j > 9)
      runSoon(callback);
    else
      runSoon(mpUntilDoneInner);
  }

  var j = 0;
  mpUntilDoneInner();
}

/*
     |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|
                                              Per-Inst   Leaked    Total      Rem      Mean       StdDev     Total      Rem      Mean       StdDev

*/
// Grab the class name and the number of remaining objects.
var bloatRex = /\s*\d+\s+(\S+)\s+\d+\s+\d+\s+\d+\s+(\d+)\s+.*/;
const SET_QUOTA = false;
const USE_QUOTA = false;

function bloatStats(callback)
{
  var objectCounts = {};

  try {
    //d.d.d;
    NetUtil.asyncFetch("about:bloat", fetched);
  } catch(e) {
    dumpln("Can't open about:bloat -- maybe you forgot to use XPCOM_MEM_LEAK_LOG");
    callback(objectCounts);
  }

  function fetched(aInputStream, aResult)
  {
    var r = NetUtil.readInputStreamToString(aInputStream, aInputStream.available());
    var lines = r.split("\n");
    for (var i = 0; i < lines.length; ++i)
    {
      var a = bloatRex.exec(lines[i]);
      if (a) {
        if (SET_QUOTA) {
          dumpln("'" + a[1] + "': " + a[2] + ",");
        } else if (USE_QUOTA) {
          quotaA = QUOTA[a[1]] || 0;
          if (a[2] > quotaA) { dumpln("Leak? Too many " + a[1] + " (" + a[2] + " > " + quotaA + ")"); }
        }
        objectCounts[a[1]] = a[2];
      }
    }
    runSoon(callCallback);
  }

  function callCallback()
  {
    callback(objectCounts)
  }
}

/********
 * QUIT *
 ********/

// From quit.js, which Bob Clary extracted from mozilla/toolkit/content

function canQuitApplication()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
    .getService(Components.interfaces.nsIObserverService);
  if (!os)
  {
    return true;
  }

  try
 {
    var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
      .createInstance(Components.interfaces.nsISupportsPRBool);
    os.notifyObservers(cancelQuit, "quit-application-requested", null);

    // Something aborted the quit process.
    if (cancelQuit.data)
    {
      return false;
    }
  }
  catch (ex)
  {
  }
  os.notifyObservers(null, "quit-application-granted", null);
  return true;
}

function goQuitApplication()
{
  dumpln("goQuitApplication (js component)");

  if (!canQuitApplication())
  {
    return false;
  }

  var kAppStartup = '@mozilla.org/toolkit/app-startup;1';
  var kAppShell   = '@mozilla.org/appshell/appShellService;1';
  var   appService;
  var   forceQuit;

  if (kAppStartup in Components.classes)
  {
    appService = Components.classes[kAppStartup].
      getService(Components.interfaces.nsIAppStartup);
    forceQuit  = Components.interfaces.nsIAppStartup.eForceQuit;

  }
  else if (kAppShell in Components.classes)
  {
    appService = Components.classes[kAppShell].
      getService(Components.interfaces.nsIAppShellService);
    forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
  }
  else
  {
    throw 'goQuitApplication: no AppStartup/appShell';
  }

  var windowManager = Components.
    classes['@mozilla.org/appshell/window-mediator;1'].getService();

  var windowManagerInterface = windowManager.
    QueryInterface(Components.interfaces.nsIWindowMediator);

  var enumerator = windowManagerInterface.getEnumerator(null);

  while (enumerator.hasMoreElements())
  {
    var domWindow = enumerator.getNext();
    if (("tryToClose" in domWindow) && !domWindow.tryToClose())
    {
      return false;
    }
    domWindow.close();
  }

  try
  {
    appService.quit(forceQuit);
  }
  catch(ex)
  {
    throw('goQuitApplication: ' + ex);
  }

  return true;
}

<|endoftext|>