<commit_before>/**
 * kreXX: Krumo eXXtended
 *
 * kreXX is a debugging tool, which displays structured information
 * about any PHP object. It is a nice replacement for print_r() or var_dump()
 * which are used by a lot of PHP developers.
 *
 * kreXX is a fork of Krumo, which was originally written by:
 * Kaloyan K. Tsvetkov <kaloyan@kaloyan.info>
 *
 * @author
 *   brainworXX GmbH <info@brainworxx.de>
 *
 * @license
 *   http://opensource.org/licenses/LGPL-2.1
 *
 *   GNU Lesser General Public License Version 2.1
 *
 *   kreXX Copyright (C) 2014-2020 Brainworxx GmbH
 *
 *   This library is free software; you can redistribute it and/or modify it
 *   under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation; either version 2.1 of the License, or (at
 *   your option) any later version.
 *   This library 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 Lesser General Public License
 *   for more details.
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software Foundation,
 *   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

(function (TYPO3) {
    "use strict";

    document.addEventListener("DOMContentLoaded", function () {
        tabs.onDocumentReady();
        formSupport.onDocumentReady();
        ajaxRefresh.onDocumentReady();
    });

    /**
     * @namespace tabs
     */
    function tabs() {}

    /**
     * Initializing the tabs handling.
     *
     * @event DOMContentLoaded
     */
    tabs.onDocumentReady = function () {
        var elements = document.querySelectorAll('.nav-tabs li');

        for (var i = 0; i < elements.length; i++) {
            elements[i].addEventListener('click', tabs.toggle);
        }
    };

    /**
     * Toggle the tab on click.
     *
     * @event click
     * @param {Event} event
     */
    tabs.toggle = function (event) {
        var activeTab = document.querySelector('.nav-tabs li.active');
        var activeContent = document.querySelector('.tab-content .tab-pane.active');

        if (activeTab !== null) {
            activeTab.classList.remove('active');
        }

        if (activeContent !== null) {
            activeContent.classList.remove('active');
        }

        var li = event.target.parentNode;

        if (li !== null) {
            li.classList.add('active');
            document.getElementById(li.getAttribute('data-tab')).classList.add('active');
        }
    };

    /**
     * @namespace formSupport
     */
    function formSupport(){}

    /**
     * Initializing the form support.
     *
     * @event DOMContentLoaded
     */
    formSupport.onDocumentReady = function () {
        var checkboxes = document.getElementsByName('tx_includekrexx_tools_includekrexxkrexxconfiguration[settings][factory]');

        for (var i = 0, n = checkboxes.length; i < n; i++) {
            formSupport.toggle(checkboxes[i]);
            checkboxes[i].addEventListener('click', function () {
                formSupport.toggle(this);
            }, true);
        }

        document.getElementById('main-save').addEventListener('click', function () {
            var form = document.getElementById('save-form');
            if (typeof form === 'object' && form !== 'undefined' && form != null) {
                form.submit();
            }
        }, true);

        document.getElementById('mode-switch').addEventListener('change', formSupport.changeMode);

        document.getElementById('clear-cookies').addEventListener('click', function () {
            // We do not delete the cookie, we simply remove all settings in it.
            /** @type {Object} */
            var settings = {};
            /** @type {Date} */
            var date = new Date();
            date.setTime(date.getTime() + (99 * 24 * 60 * 60 * 1000));
            /** @type {string} */
            var expires = 'expires=' + date.toUTCString();
            document.cookie = 'KrexxDebugSettings=' + JSON.stringify(settings) + '; ' + expires + '; path=/';

            ajaxRefresh.message({
                class: 'success',
                text: 'All cookie settings were removed!'
            })
        }, true);
    };

    /**
     * When activating the checkbox behind the form element, we deactivat the
     * form element, belonging to the checkbox.
     *
     * @param {HTMLInputElement} checkbox
     */
    formSupport.toggle = function (checkbox) {
        // Find the corresponding element.
        var id = checkbox.id.split('.');
        if (typeof id[1] !== 'undefined') {
            var element = document.getElementById(id[1]);
            var fallback = document.getElementById(id[1] + '-fallback');
        }

        if (typeof element === 'object' && typeof element !== 'undefined' && element != null) {
            if (checkbox.checked) {
                element.disabled = true;
                element.parentNode.classList.remove('active');
                fallback.classList.remove('disabled');
            } else {
                element.disabled = false;
                element.parentNode.classList.add('active');
                fallback.classList.add('disabled');
            }
        }
    };

    /**
     * We simply set a class on the higher dom elements to show the expert settings, if desired.
     *
     * @param {Event} event
     */
    formSupport.changeMode = function (event) {
        var modeSwitch = document.getElementById('mode-switch');
        var value = modeSwitch.options[modeSwitch.selectedIndex].value;

        if (value === 'expert') {
            document.getElementById('typo3-inner-docbody').classList.add('expert-mode');
        } else {
            document.getElementById('typo3-inner-docbody').classList.remove('expert-mode');
        }
    };

    /**
     * The ajax related stuff.
     *
     * @namespace ajaxRefresh
     */
    function ajaxRefresh() {}

    /**
     * Initializing the ajax handling
     *
     * @event DOMContentLoaded
     */
    ajaxRefresh.onDocumentReady = function () {
        // Get a first impression.
        ajaxRefresh.call();

        // Fetch a new list every 5 seconds.
        ajaxRefresh.timeout();

        // Register the bubbelung eventhandler for the delete button.
        document.querySelector('table.krexx-logs').addEventListener('click', ajaxRefresh.deleteHandling);
    };

    /**
     * Timeout method for refreshing the logfile list.
     */
    ajaxRefresh.timeout = function () {
        setTimeout(function () {
            ajaxRefresh.call();
            ajaxRefresh.timeout();
        }, 5000);
    };

    /**
     * We store the last answer, to check if there was an update.
     *
     * @type {string}
     */
    ajaxRefresh.lastAnswer = '';

    /**
     * Refresh the logfile list.
     */
    ajaxRefresh.call = function () {
        var request = new XMLHttpRequest();
        request.open("GET", TYPO3.settings.ajaxUrls['includekrexx_refresh'], true);

        var table = document.querySelector('table.krexx-logs tbody');
        request.onload = function (event) {
            var result = JSON.parse(request.responseText);

            // Are there any logiles, at all?
            if (result.length === 0) {
                document.querySelector('#tab-1 .table-wrapper').classList.add('display-none');
                document.querySelector('#tab-1 .noresult').classList.remove('display-none');
                // Nothing more to do here.
                return;
            }

            document.querySelector('#tab-1 .table-wrapper').classList.remove('display-none');
            document.querySelector('#tab-1 .noresult').classList.add('display-none');

            var html = ajaxRefresh.generateHtml(result);

            if (ajaxRefresh.lastAnswer !== html) {
                table.innerHTML = html;
                ajaxRefresh.message({
                    text: 'Updated the log-list!',
                    class: 'success'
                });
            }

            ajaxRefresh.lastAnswer = html;

        };

        request.send();
    };

    /**
     * Generate the HTML for the file list.
     *
     * @param result
     * @return {string}
     */
    ajaxRefresh.generateHtml = function (result) {
        var html = '';

        for (var key in result) {
            if (!result.hasOwnProperty(key)) {
                continue;
            }

            var file = result[key];
            html += '
';
            html += '

	';
            html += '  ' + file.name + '	';

            html += '';
            for (var i = 0; i < file.meta.length; i++) {
                html += '' + file.meta[i].type + '
';
                html += 'in ' + file.meta[i].filename + ', line ' + file.meta[i].line;
                if (i < file.meta.length - 1) {
                    html += '

';
                }
            }
            if (file.meta.length > 0) {
                html += '

'
            }
            html += '	';

            html += '' + file.time + '	' + file.size + '	';
            html += '

';
        }

        return html;
    };

    /**
     * Generate the background style from the time string.
     *
     * @param string string
     *
     * @return string
     *   style=" background-color: rgba(255, 0, 0, 0.1);"
     */
    ajaxRefresh.generateBackgroundStyle = function (string) {
        var chr, hash, i, values;

        for (i = 0; i < string.length; i++) {
            chr   = string.charCodeAt(i);
            hash  = ((hash << 5) - hash) + chr;
            // Convert to 32bit integer
            hash |= 0;
        }

        values = Math.abs(hash).toString().match(/.{1,2}/g);

        return 'style=" background-color: rgba(' + values[3] + ', ' + values[2] + ', ' + values[1] + ', 0.2' + values[0] + ');"';
    };

    /**
     * Delete click handler for the delete list.
     *
     * @param event
     */
    ajaxRefresh.deleteHandling = function (event) {
        var target = event.target;
        var id;

        // Retrieve the id.
        if (target.hasAttribute('data-id')) {
            id = target.getAttribute('data-id')
        } else if (target.parentElement.hasAttribute('data-id')) {
            id = target.parentElement.getAttribute('data-id')
        }

        // Found anything?
        if (typeof id === 'undefined' || id === null) {
            // No id found. Early return.
            return;
        }

        var confirmed = confirm("Delete this file?");

        if (confirmed === false) {
            return;
        }

        var request = new XMLHttpRequest();
        request.open("GET", TYPO3.settings.ajaxUrls['includekrexx_delete'] + '&fileid=' + id, true);

        request.onload = function (event) {
            try {
                ajaxRefresh.message(JSON.parse(request.responseText));
            } catch (e) {
                // We are not going to output the answer from the server, although
                // it is visible in the browser inspector.
                ajaxRefresh.message({class: 'error', text: 'Error while parsing the server answer.'});
            }

            ajaxRefresh.call();
        };

        request.send();
    };

    /**
     * Ajax relaqted messaging.
     *
     * @param {{}} json
     */
    ajaxRefresh.message = function (json) {
        var messagebox = document.querySelector('.message-container-outer');
        var message = document.createElement("div");

        message.classList.add('ajax-msg');
        message.classList.add(json.class);
        message.innerHTML = '

' + json.text + '
';
        messagebox.appendChild(message);

        setTimeout(function () {
            message.classList.add('fade');
            setTimeout(function () {
                message.classList.add('remove');
                setTimeout(function () {
                    messagebox.removeChild(message);
                }, 600);
            }, 600);
        }, 4000);
    }

})(TYPO3);<commit_msg>Removed a similar block.<commit_after>/**
 * kreXX: Krumo eXXtended
 *
 * kreXX is a debugging tool, which displays structured information
 * about any PHP object. It is a nice replacement for print_r() or var_dump()
 * which are used by a lot of PHP developers.
 *
 * kreXX is a fork of Krumo, which was originally written by:
 * Kaloyan K. Tsvetkov <kaloyan@kaloyan.info>
 *
 * @author
 *   brainworXX GmbH <info@brainworxx.de>
 *
 * @license
 *   http://opensource.org/licenses/LGPL-2.1
 *
 *   GNU Lesser General Public License Version 2.1
 *
 *   kreXX Copyright (C) 2014-2020 Brainworxx GmbH
 *
 *   This library is free software; you can redistribute it and/or modify it
 *   under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation; either version 2.1 of the License, or (at
 *   your option) any later version.
 *   This library 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 Lesser General Public License
 *   for more details.
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software Foundation,
 *   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

(function (TYPO3) {
    "use strict";

    document.addEventListener("DOMContentLoaded", function () {
        tabs.onDocumentReady();
        formSupport.onDocumentReady();
        ajaxRefresh.onDocumentReady();
    });

    /**
     * @namespace tabs
     */
    function tabs() {}

    /**
     * Initializing the tabs handling.
     *
     * @event DOMContentLoaded
     */
    tabs.onDocumentReady = function () {
        var elements = document.querySelectorAll('.nav-tabs li');

        for (var i = 0; i < elements.length; i++) {
            elements[i].addEventListener('click', tabs.toggle);
        }
    };

    /**
     * Toggle the tab on click.
     *
     * @event click
     * @param {Event} event
     */
    tabs.toggle = function (event) {
        var activeTab = document.querySelector('.nav-tabs li.active');
        var activeContent = document.querySelector('.tab-content .tab-pane.active');

        if (activeTab !== null) {
            activeTab.classList.remove('active');
        }

        if (activeContent !== null) {
            activeContent.classList.remove('active');
        }

        var li = event.target.parentNode;

        if (li !== null) {
            li.classList.add('active');
            document.getElementById(li.getAttribute('data-tab')).classList.add('active');
        }
    };

    /**
     * @namespace formSupport
     */
    function formSupport(){}

    /**
     * Initializing the form support.
     *
     * @event DOMContentLoaded
     */
    formSupport.onDocumentReady = function () {
        var checkboxes = document.getElementsByName('tx_includekrexx_tools_includekrexxkrexxconfiguration[settings][factory]');

        for (var i = 0, n = checkboxes.length; i < n; i++) {
            formSupport.toggle(checkboxes[i]);
            checkboxes[i].addEventListener('click', function () {
                formSupport.toggle(this);
            }, true);
        }

        document.getElementById('main-save').addEventListener('click', function () {
            var form = document.getElementById('save-form');
            if (typeof form === 'object' && form !== 'undefined' && form != null) {
                form.submit();
            }
        }, true);

        document.getElementById('mode-switch').addEventListener('change', formSupport.changeMode);

        document.getElementById('clear-cookies').addEventListener('click', function () {
            // We do not delete the cookie, we simply remove all settings in it.
            /** @type {Object} */
            var settings = {};
            /** @type {Date} */
            var date = new Date();
            date.setTime(date.getTime() + (99 * 24 * 60 * 60 * 1000));
            /** @type {string} */
            var expires = 'expires=' + date.toUTCString();
            document.cookie = 'KrexxDebugSettings=' + JSON.stringify(settings) + '; ' + expires + '; path=/';

            ajaxRefresh.message({
                class: 'success',
                text: 'All cookie settings were removed!'
            })
        }, true);
    };

    /**
     * When activating the checkbox behind the form element, we deactivat the
     * form element, belonging to the checkbox.
     *
     * @param {HTMLInputElement} checkbox
     */
    formSupport.toggle = function (checkbox) {
        // Find the corresponding element.
        var id = checkbox.id.split('.');
        if (typeof id[1] !== 'undefined') {
            var element = document.getElementById(id[1]);
            var fallback = document.getElementById(id[1] + '-fallback');
        }

        if (typeof element === 'object' && typeof element !== 'undefined' && element != null) {
            if (checkbox.checked) {
                element.disabled = true;
                element.parentNode.classList.remove('active');
                fallback.classList.remove('disabled');
            } else {
                element.parentNode.classList.add('active');
                element.disabled = false;
                fallback.classList.add('disabled');
            }
        }
    };

    /**
     * We simply set a class on the higher dom elements to show the expert settings, if desired.
     *
     * @param {Event} event
     */
    formSupport.changeMode = function (event) {
        var modeSwitch = document.getElementById('mode-switch');
        var value = modeSwitch.options[modeSwitch.selectedIndex].value;

        if (value === 'expert') {
            document.getElementById('typo3-inner-docbody').classList.add('expert-mode');
        } else {
            document.getElementById('typo3-inner-docbody').classList.remove('expert-mode');
        }
    };

    /**
     * The ajax related stuff.
     *
     * @namespace ajaxRefresh
     */
    function ajaxRefresh() {}

    /**
     * Initializing the ajax handling
     *
     * @event DOMContentLoaded
     */
    ajaxRefresh.onDocumentReady = function () {
        // Get a first impression.
        ajaxRefresh.call();

        // Fetch a new list every 5 seconds.
        ajaxRefresh.timeout();

        // Register the bubbelung eventhandler for the delete button.
        document.querySelector('table.krexx-logs').addEventListener('click', ajaxRefresh.deleteHandling);
    };

    /**
     * Timeout method for refreshing the logfile list.
     */
    ajaxRefresh.timeout = function () {
        setTimeout(function () {
            ajaxRefresh.call();
            ajaxRefresh.timeout();
        }, 5000);
    };

    /**
     * We store the last answer, to check if there was an update.
     *
     * @type {string}
     */
    ajaxRefresh.lastAnswer = '';

    /**
     * Refresh the logfile list.
     */
    ajaxRefresh.call = function () {
        var request = new XMLHttpRequest();
        request.open("GET", TYPO3.settings.ajaxUrls['includekrexx_refresh'], true);

        var table = document.querySelector('table.krexx-logs tbody');
        request.onload = function (event) {
            var result = JSON.parse(request.responseText);

            // Are there any logiles, at all?
            if (result.length === 0) {
                document.querySelector('#tab-1 .table-wrapper').classList.add('display-none');
                document.querySelector('#tab-1 .noresult').classList.remove('display-none');
                // Nothing more to do here.
                return;
            }

            document.querySelector('#tab-1 .table-wrapper').classList.remove('display-none');
            document.querySelector('#tab-1 .noresult').classList.add('display-none');

            var html = ajaxRefresh.generateHtml(result);

            if (ajaxRefresh.lastAnswer !== html) {
                table.innerHTML = html;
                ajaxRefresh.message({
                    text: 'Updated the log-list!',
                    class: 'success'
                });
            }

            ajaxRefresh.lastAnswer = html;

        };

        request.send();
    };

    /**
     * Generate the HTML for the file list.
     *
     * @param result
     * @return {string}
     */
    ajaxRefresh.generateHtml = function (result) {
        var html = '';

        for (var key in result) {
            if (!result.hasOwnProperty(key)) {
                continue;
            }

            var file = result[key];
            html += '
';
            html += '

	';
            html += '  ' + file.name + '	';

            html += '';
            for (var i = 0; i < file.meta.length; i++) {
                html += '' + file.meta[i].type + '
';
                html += 'in ' + file.meta[i].filename + ', line ' + file.meta[i].line;
                if (i < file.meta.length - 1) {
                    html += '

';
                }
            }
            if (file.meta.length > 0) {
                html += '

'
            }
            html += '	';

            html += '' + file.time + '	' + file.size + '	';
            html += '

';
        }

        return html;
    };

    /**
     * Generate the background style from the time string.
     *
     * @param string string
     *
     * @return string
     *   style=" background-color: rgba(255, 0, 0, 0.1);"
     */
    ajaxRefresh.generateBackgroundStyle = function (string) {
        var chr, hash, i, values;

        for (i = 0; i < string.length; i++) {
            chr   = string.charCodeAt(i);
            hash  = ((hash << 5) - hash) + chr;
            // Convert to 32bit integer
            hash |= 0;
        }

        values = Math.abs(hash).toString().match(/.{1,2}/g);

        return 'style=" background-color: rgba(' + values[3] + ', ' + values[2] + ', ' + values[1] + ', 0.2' + values[0] + ');"';
    };

    /**
     * Delete click handler for the delete list.
     *
     * @param event
     */
    ajaxRefresh.deleteHandling = function (event) {
        var target = event.target;
        var id;

        // Retrieve the id.
        if (target.hasAttribute('data-id')) {
            id = target.getAttribute('data-id')
        } else if (target.parentElement.hasAttribute('data-id')) {
            id = target.parentElement.getAttribute('data-id')
        }

        // Found anything?
        if (typeof id === 'undefined' || id === null) {
            // No id found. Early return.
            return;
        }

        var confirmed = confirm("Delete this file?");

        if (confirmed === false) {
            return;
        }

        var request = new XMLHttpRequest();
        request.open("GET", TYPO3.settings.ajaxUrls['includekrexx_delete'] + '&fileid=' + id, true);

        request.onload = function (event) {
            try {
                ajaxRefresh.message(JSON.parse(request.responseText));
            } catch (e) {
                // We are not going to output the answer from the server, although
                // it is visible in the browser inspector.
                ajaxRefresh.message({class: 'error', text: 'Error while parsing the server answer.'});
            }

            ajaxRefresh.call();
        };

        request.send();
    };

    /**
     * Ajax relaqted messaging.
     *
     * @param {{}} json
     */
    ajaxRefresh.message = function (json) {
        var messagebox = document.querySelector('.message-container-outer');
        var message = document.createElement("div");

        message.classList.add('ajax-msg');
        message.classList.add(json.class);
        message.innerHTML = '

' + json.text + '
';
        messagebox.appendChild(message);

        setTimeout(function () {
            message.classList.add('fade');
            setTimeout(function () {
                message.classList.add('remove');
                setTimeout(function () {
                    messagebox.removeChild(message);
                }, 600);
            }, 600);
        }, 4000);
    }

})(TYPO3);<|endoftext|>


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

/**
 * @fileoverview Utility functions for handling variables.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * @name Blockly.Variables
 * @namespace
 **/
goog.provide('Blockly.Variables');

goog.require('Blockly.Blocks');
goog.require('Blockly.constants');
goog.require('Blockly.Workspace');
goog.require('goog.string');

/**
 * Find all user-created variables that are in use in the workspace.
 * For use by generators.
 * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace.
 * @return {!Array.<string>} Array of variable names.
 */
Blockly.Variables.allUsedVariables = function(root) {
  var blocks;
  if (root instanceof Blockly.Block) {
    // Root is Block.
    blocks = root.getDescendants();
  } else if (root.getAllBlocks) {
    // Root is Workspace.
    blocks = root.getAllBlocks();
  } else {
    throw 'Not Block or Workspace: ' + root;
  }
  var variableHash = Object.create(null);
  // Iterate through every block and add each variable to the hash.
  for (var x = 0; x < blocks.length; x++) {
    var blockVariables = blocks[x].getVars();
    if (blockVariables) {
      for (var y = 0; y < blockVariables.length; y++) {
        var varName = blockVariables[y];
        // Variable name may be null if the block is only half-built.
        if (varName) {
          variableHash[varName.toLowerCase()] = varName;
        }
      }
    }
  }
  // Flatten the hash into a list.
  var variableList = [];
  for (var name in variableHash) {
    variableList.push(variableHash[name]);
  }
  return variableList;
};

/**
 * Find all variables that the user has created through the workspace or
 * toolbox.  For use by generators.
 * @param {!Blockly.Workspace} root The workspace to inspect.
 * @return {!Array.<string>} Array of variable names.
 */
Blockly.Variables.allVariables = function(root) {
  if (root instanceof Blockly.Block) {
    // Root is Block.
    console.warn('Deprecated call to Blockly.Variables.allVariables ' +
                 'with a block instead of a workspace.  You may want ' +
                 'Blockly.Variables.allUsedVariables');
  }
  return root.variableList;
};

/**
 * Construct the blocks required by the flyout for the variable category.
 * @param {!Blockly.Workspace} workspace The workspace contianing variables.
 * @return {!Array.<!Element>} Array of XML block elements.
 */
Blockly.Variables.flyoutCategory = function(workspace) {
  var variableList = workspace.variableList;
  variableList.sort(goog.string.caseInsensitiveCompare);

  var xmlList = [];
  var button = goog.dom.createDom('button');
  button.setAttribute('text', Blockly.Msg.NEW_VARIABLE);
  button.setAttribute('callbackKey', 'CREATE_VARIABLE');

  workspace.registerButtonCallback('CREATE_VARIABLE', function(button) {
    Blockly.Variables.createVariable(button.getTargetWorkspace());
  });

  xmlList.push(button);

  if (variableList.length > 0) {
    if (Blockly.Blocks['variables_set']) {
      // <block type="variables_set" gap="20">
      //   <field name="VAR">item</field>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'variables_set');
      if (Blockly.Blocks['math_change']) {
        block.setAttribute('gap', 8);
      } else {
        block.setAttribute('gap', 24);
      }
      var field = goog.dom.createDom('field', null, variableList[0]);
      field.setAttribute('name', 'VAR');
      block.appendChild(field);
      xmlList.push(block);
    }
    if (Blockly.Blocks['math_change']) {
      // <block type="math_change">
      //   <value name="DELTA">
      //     <shadow type="math_number">
      //       <field name="NUM">1</field>
      //     </shadow>
      //   </value>
      // </block>
      var block = goog.dom.createDom('block');
      block.setAttribute('type', 'math_change');
      if (Blockly.Blocks['variables_get']) {
        block.setAttribute('gap', 20);
      }
      var value = goog.dom.createDom('value');
      value.setAttribute('name', 'DELTA');
      block.appendChild(value);

      var field = goog.dom.createDom('field', null, variableList[0]);
      field.setAttribute('name', 'VAR');
      block.appendChild(field);

      var shadowBlock = goog.dom.createDom('shadow');
      shadowBlock.setAttribute('type', 'math_number');
      value.appendChild(shadowBlock);

      var numberField = goog.dom.createDom('field', null, '1');
      numberField.setAttribute('name', 'NUM');
      shadowBlock.appendChild(numberField);

      xmlList.push(block);
    }

    for (var i = 0; i < variableList.length; i++) {
      if (Blockly.Blocks['variables_get']) {
        // <block type="variables_get" gap="8">
        //   <field name="VAR">item</field>
        // </block>
        var block = goog.dom.createDom('block');
        block.setAttribute('type', 'variables_get');
        if (Blockly.Blocks['variables_set']) {
          block.setAttribute('gap', 8);
        }
        var field = goog.dom.createDom('field', null, variableList[i]);
        field.setAttribute('name', 'VAR');
        block.appendChild(field);
        xmlList.push(block);
      }
    }
  }
  return xmlList;
};

/**
* Return a new variable name that is not yet being used. This will try to
* generate single letter variable names in the range 'i' to 'z' to start with.
* If no unique name is located it will try 'i' to 'z', 'a' to 'h',
* then 'i2' to 'z2' etc.  Skip 'l'.
 * @param {!Blockly.Workspace} workspace The workspace to be unique in.
* @return {string} New variable name.
*/
Blockly.Variables.generateUniqueName = function(workspace) {
  var variableList = workspace.variableList;
  var newName = '';
  if (variableList.length) {
    var nameSuffix = 1;
    var letters = 'ijkmnopqrstuvwxyzabcdefgh';  // No 'l'.
    var letterIndex = 0;
    var potName = letters.charAt(letterIndex);
    while (!newName) {
      var inUse = false;
      for (var i = 0; i < variableList.length; i++) {
        if (variableList[i].toLowerCase() == potName) {
          // This potential name is already used.
          inUse = true;
          break;
        }
      }
      if (inUse) {
        // Try the next potential name.
        letterIndex++;
        if (letterIndex == letters.length) {
          // Reached the end of the character sequence so back to 'i'.
          // a new suffix.
          letterIndex = 0;
          nameSuffix++;
        }
        potName = letters.charAt(letterIndex);
        if (nameSuffix > 1) {
          potName += nameSuffix;
        }
      } else {
        // We can use the current potential name.
        newName = potName;
      }
    }
  } else {
    newName = 'i';
  }
  return newName;
};

/**
 * Create a new variable on the given workspace.
 * @param {!Blockly.Workspace} workspace The workspace on which to create the
 *     variable.
 * @param {function(?string=)=} opt_callback A callback. It will
 *     be passed an acceptable new variable name, or null if change is to be
 *     aborted (cancel button), or undefined if an existing variable was chosen.
 */
Blockly.Variables.createVariable = function(workspace, opt_callback) {
  var promptAndCheckWithAlert = function(defaultName) {
    Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName,
      function(text) {
        if (text) {
          if (workspace.variableIndexOf(text) != -1) {
            Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
                text.toLowerCase()),
                function() {
                  promptAndCheckWithAlert(text);  // Recurse
                });
          } else {
            workspace.createVariable(text);
            if (opt_callback) {
              opt_callback(text);
            }
          }
        } else {
          // User canceled prompt without a value.
          if (opt_callback) {
            opt_callback(null);
          }
        }
      });
  };
  promptAndCheckWithAlert('');
};

/**
 * Prompt the user for a new variable name.
 * @param {string} promptText The string of the prompt.
 * @param {string} defaultText The default value to show in the prompt's field.
 * @param {function(?string)} callback A callback. It will return the new
 *     variable name, or null if the user picked something illegal.
 */
Blockly.Variables.promptName = function(promptText, defaultText, callback) {
  Blockly.prompt(promptText, defaultText, function(newVar) {
    // Merge runs of whitespace.  Strip leading and trailing whitespace.
    // Beyond this, all names are legal.
    if (newVar) {
      newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
      if (newVar == Blockly.Msg.RENAME_VARIABLE ||
          newVar == Blockly.Msg.NEW_VARIABLE) {
        // Ok, not ALL names are legal...
        newVar = null;
      }
    }
    callback(newVar);
  });
};
<commit_msg>Make it easier to read the code that creates the variable category in the tolbox<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Utility functions for handling variables.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * @name Blockly.Variables
 * @namespace
 **/
goog.provide('Blockly.Variables');

goog.require('Blockly.Blocks');
goog.require('Blockly.constants');
goog.require('Blockly.Workspace');
goog.require('goog.string');

/**
 * Find all user-created variables that are in use in the workspace.
 * For use by generators.
 * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace.
 * @return {!Array.<string>} Array of variable names.
 */
Blockly.Variables.allUsedVariables = function(root) {
  var blocks;
  if (root instanceof Blockly.Block) {
    // Root is Block.
    blocks = root.getDescendants();
  } else if (root.getAllBlocks) {
    // Root is Workspace.
    blocks = root.getAllBlocks();
  } else {
    throw 'Not Block or Workspace: ' + root;
  }
  var variableHash = Object.create(null);
  // Iterate through every block and add each variable to the hash.
  for (var x = 0; x < blocks.length; x++) {
    var blockVariables = blocks[x].getVars();
    if (blockVariables) {
      for (var y = 0; y < blockVariables.length; y++) {
        var varName = blockVariables[y];
        // Variable name may be null if the block is only half-built.
        if (varName) {
          variableHash[varName.toLowerCase()] = varName;
        }
      }
    }
  }
  // Flatten the hash into a list.
  var variableList = [];
  for (var name in variableHash) {
    variableList.push(variableHash[name]);
  }
  return variableList;
};

/**
 * Find all variables that the user has created through the workspace or
 * toolbox.  For use by generators.
 * @param {!Blockly.Workspace} root The workspace to inspect.
 * @return {!Array.<string>} Array of variable names.
 */
Blockly.Variables.allVariables = function(root) {
  if (root instanceof Blockly.Block) {
    // Root is Block.
    console.warn('Deprecated call to Blockly.Variables.allVariables ' +
                 'with a block instead of a workspace.  You may want ' +
                 'Blockly.Variables.allUsedVariables');
  }
  return root.variableList;
};

/**
 * Construct the blocks required by the flyout for the variable category.
 * @param {!Blockly.Workspace} workspace The workspace contianing variables.
 * @return {!Array.<!Element>} Array of XML block elements.
 */
Blockly.Variables.flyoutCategory = function(workspace) {
  var variableList = workspace.variableList;
  variableList.sort(goog.string.caseInsensitiveCompare);

  var xmlList = [];
  var button = goog.dom.createDom('button');
  button.setAttribute('text', Blockly.Msg.NEW_VARIABLE);
  button.setAttribute('callbackKey', 'CREATE_VARIABLE');

  workspace.registerButtonCallback('CREATE_VARIABLE', function(button) {
    Blockly.Variables.createVariable(button.getTargetWorkspace());
  });

  xmlList.push(button);

  if (variableList.length > 0) {
    if (Blockly.Blocks['variables_set']) {
      var gap = Blockly.Blocks['math_change'] ? 8 : 24;
      var blockText = '<xml>' +
            '<block type="variables_set" gap="' + gap + '">' +
            '<field name="VAR">' + variableList[0] + '</field>' +
            '</block>' +
            '</xml>';
      var block = Blockly.Xml.textToDom(blockText).firstChild;
      xmlList.push(block);
    }
    if (Blockly.Blocks['math_change']) {
      var gap = Blockly.Blocks['variables_get'] ? 20 : 8;
      var blockText = '<xml>' +
          '<block type="math_change" gap="' + gap + '">' +
          '<field name="VAR">' + variableList[0] + '</field>' +
          '<value name="DELTA">' +
          '<shadow type="math_number">' +
          '<field name="NUM">1</field>' +
          '</shadow>' +
          '</value>' +
          '</block>' +
          '</xml>';
      var block = Blockly.Xml.textToDom(blockText).firstChild;
      xmlList.push(block);
    }

    for (var i = 0; i < variableList.length; i++) {
      if (Blockly.Blocks['variables_get']) {
        var blockText = '<xml>' +
            '<block type="variables_get" gap="8">' +
            '<field name="VAR">' + variableList[i] + '</field>' +
            '</block>' +
            '</xml>';
        var block = Blockly.Xml.textToDom(blockText).firstChild;
        xmlList.push(block);
      }
    }
  }
  return xmlList;
};

/**
* Return a new variable name that is not yet being used. This will try to
* generate single letter variable names in the range 'i' to 'z' to start with.
* If no unique name is located it will try 'i' to 'z', 'a' to 'h',
* then 'i2' to 'z2' etc.  Skip 'l'.
 * @param {!Blockly.Workspace} workspace The workspace to be unique in.
* @return {string} New variable name.
*/
Blockly.Variables.generateUniqueName = function(workspace) {
  var variableList = workspace.variableList;
  var newName = '';
  if (variableList.length) {
    var nameSuffix = 1;
    var letters = 'ijkmnopqrstuvwxyzabcdefgh';  // No 'l'.
    var letterIndex = 0;
    var potName = letters.charAt(letterIndex);
    while (!newName) {
      var inUse = false;
      for (var i = 0; i < variableList.length; i++) {
        if (variableList[i].toLowerCase() == potName) {
          // This potential name is already used.
          inUse = true;
          break;
        }
      }
      if (inUse) {
        // Try the next potential name.
        letterIndex++;
        if (letterIndex == letters.length) {
          // Reached the end of the character sequence so back to 'i'.
          // a new suffix.
          letterIndex = 0;
          nameSuffix++;
        }
        potName = letters.charAt(letterIndex);
        if (nameSuffix > 1) {
          potName += nameSuffix;
        }
      } else {
        // We can use the current potential name.
        newName = potName;
      }
    }
  } else {
    newName = 'i';
  }
  return newName;
};

/**
 * Create a new variable on the given workspace.
 * @param {!Blockly.Workspace} workspace The workspace on which to create the
 *     variable.
 * @param {function(?string=)=} opt_callback A callback. It will
 *     be passed an acceptable new variable name, or null if change is to be
 *     aborted (cancel button), or undefined if an existing variable was chosen.
 */
Blockly.Variables.createVariable = function(workspace, opt_callback) {
  var promptAndCheckWithAlert = function(defaultName) {
    Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName,
      function(text) {
        if (text) {
          if (workspace.variableIndexOf(text) != -1) {
            Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
                text.toLowerCase()),
                function() {
                  promptAndCheckWithAlert(text);  // Recurse
                });
          } else {
            workspace.createVariable(text);
            if (opt_callback) {
              opt_callback(text);
            }
          }
        } else {
          // User canceled prompt without a value.
          if (opt_callback) {
            opt_callback(null);
          }
        }
      });
  };
  promptAndCheckWithAlert('');
};

/**
 * Prompt the user for a new variable name.
 * @param {string} promptText The string of the prompt.
 * @param {string} defaultText The default value to show in the prompt's field.
 * @param {function(?string)} callback A callback. It will return the new
 *     variable name, or null if the user picked something illegal.
 */
Blockly.Variables.promptName = function(promptText, defaultText, callback) {
  Blockly.prompt(promptText, defaultText, function(newVar) {
    // Merge runs of whitespace.  Strip leading and trailing whitespace.
    // Beyond this, all names are legal.
    if (newVar) {
      newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
      if (newVar == Blockly.Msg.RENAME_VARIABLE ||
          newVar == Blockly.Msg.NEW_VARIABLE) {
        // Ok, not ALL names are legal...
        newVar = null;
      }
    }
    callback(newVar);
  });
};
<|endoftext|>


<commit_before>/*global window */
/*global console */
/*global XMLHttpRequest */
/*global XDomainRequest */
/*global Blob */
/*global setTimeout */
/*global clearTimeout */
/*global DOMParser */
/*global Element */
/*global document */

if(typeof(global) !== 'undefined' && typeof(nodeRequire) === 'undefined') {
  var nodeRequire = require;
}

define(['./util'], function(util) {

  "use strict";

  // Namespace: platform
  //
  // Platform specific implementations of common things to do.
  //
  // Method: ajax
  //
  // Set off an HTTP request.
  // Uses CORS, if available on the platform.
  //
  // Parameters:
  //   (given as an *Object*)
  //
  //   url     - URL to send the request to
  //   success - callback function to call when request succeeded
  //   error   - callback function to call when request failed
  //   method  - (optional) HTTP request method to use (default: GET)
  //   headers - (optional) object containing request headers to set
  //   timeout - (optional) milliseconds until request is given up and error
  //             callback is called. If omitted, the request never times out.
  //
  // Example:
  //   (start code)
  //   platform.ajax({
  //     url: "http://en.wikipedia.org/wiki/AJAX",
  //     success: function(responseText, responseHeaders) {
  //       console.log("Here's the page: ", responseText);
  //     },
  //     error: function(errorMessage) {
  //       console.error("Something went wrong: ", errorMessage);
  //     },
  //     timeout: 3000
  //   });
  //   (end code)
  //
  // Platform support:
  //   web browser - YES (if browser <supports CORS at http://caniuse.com/#feat=cors>)
  //   IE - Partially, no support for setting headers.
  //   node - YES, CORS not an issue at all
  //
  //
  // Method: parseXml
  //
  // Parse given XML source.
  //
  // Platform support:
  //   browser - yes, if DOMParser is available
  //   node - yes, if xml2js is available
  //
  //
  // Method: setElementHTML
  //
  // Set the HTML content of an element.
  //
  // Parameters:
  //   element - either an Element or a DOM ID resolving to an element
  //   content - HTML content to set
  //
  // Platform support:
  //   browser - Yes
  //   node - not implemented
  //
  //
  // Method: getElementValue
  //
  //
  // Method: eltOn
  //
  //
  // Method: getLocation
  //
  //
  // Method: setLocation
  //
  //
  // Method: alert
  //

  var logger = util.getLogger('platform');

  // downcase all header keys
  function normalizeHeaders(headers) {
    var h = {};
    for(var key in headers) {
      h[key.toLowerCase()] = headers[key];
    }
    return h;
  }

  function browserParseHeaders(rawHeaders) {
    if(! rawHeaders) {
      // firefox bug. workaround in ajaxBrowser.
      return null;
    }
    var headers = {};
    var lines = rawHeaders.split(/\r?\n/);
    var lastKey = null, md, key, value;
    var numLines = lines.length;
    for(var i=0;i<numLines;i++) {
      if(lines[i].length === 0) {
        // empty line. obviously.
        continue;
      } else if((md = lines[i].match(/^([^:]+):\s*(.+)$/))) {
        // The escaped colon in the following (previously added) comment is
        // necessary, to prevent NaturalDocs from generating a toplevel
        // document called "value line" to the documentation. True story.

        // key\: value line
        key = md[1], value = md[2];
        headers[key] = value;
        lastKey = key;
      } else if((md = lines[i].match(/^\s+(.+)$/))) {
        // continued line (if previous line exceeded 80 bytes
        key = lastKey, value= md[1];
        headers[key] = headers[key] + value;
      } else {
        // nothing we recognize.
        logger.error("Failed to parse header line: " + lines[i]);
      }
    }
    return normalizeHeaders(headers);
  }

  var successStates = { 200:true, 201:true, 204:true, 207:true };

  function isSuccessful(xhr) {
    return !! successStates[xhr.status];
  }

  function ajaxBrowser(params) {
    return util.makePromise(function(promise) {
      var timedOut = false;
      var timer;
      var xhr = new XMLHttpRequest();
      if(params.timeout) {
        timer = window.setTimeout(function() {
          timedOut = true;
          xhr.abort();
          promise.fail('timeout');
        }, params.timeout);
      }

      if(!params.method) {
        params.method = 'GET';
      }

      xhr.open(params.method, params.url, true);

      if(params.headers) {
        for(var header in params.headers) {
          xhr.setRequestHeader(header, params.headers[header]);
        }
      }

      xhr.onreadystatechange = function() {
        if((xhr.readyState == 4)) {
          if(timer) {
            window.clearTimeout(timer);
          }
          if(isSuccessful(xhr)) {
            logger.debug("REQUEST SUCCESSFUL", params.url, xhr.responseText);
            var headers = browserParseHeaders(xhr.getAllResponseHeaders());
            if(! headers) {
              // Firefox' getAllResponseHeaders is broken for CORS requests since forever.
              // https://bugzilla.mozilla.org/show_bug.cgi?id=608735
              // Any additional headers that are needed by other code, should be added here.
              headers = {
                'content-type': xhr.getResponseHeader('Content-Type')
              };
            }
            promise.fulfill(xhr.responseText, headers);
          } else {
            logger.debug("REQUEST FAILED", xhr.status, params.url);
            promise.fail(xhr.status || new Error('network error'));
          }
        }
      };

      if(typeof(params.data) === 'string') {
        xhr.send(params.data);
      } else {
        xhr.send();
      }
    });
  }

  function ajaxExplorer(params) {
    // this won't work, because we have no way of sending
    // the Authorization header. It might work for GET to
    // the 'public' category, though.
    var promise = util.getPromise();
    var xdr = new XDomainRequest();
    xdr.timeout = params.timeout || 3000;//is this milliseconds? documentation doesn't say
    xdr.open(params.method, params.url);
    xdr.onload = function() {
      if(xdr.status == 200 || xdr.status == 201 || xdr.status == 204) {
        promise.fulfill(xdr.responseText);
      } else {
        params.fail(xdr.status);
      }
    };
    xdr.onerror = function() {
      // See http://msdn.microsoft.com/en-us/library/ms536930%28v=vs.85%29.aspx
      promise.fail(new Error('unknown error'));
    };
    xdr.ontimeout = function() {
      promise.fail('timeout');
    };
    if(params.data) {
      xdr.send(params.data);
    } else {
      xdr.send();
    }
  }

  function ajaxNode(params) {

    if(typeof(params.data) === 'object' && params.data instanceof Blob) {
      throw new Error("Sending binary data not yet implemented for nodejs");
    }

    var http=nodeRequire('http'),
      https=nodeRequire('https'),
      url=nodeRequire('url');
    if(!params.method) {
      params.method='GET';
    }
    if(params.data) {
      params.headers['content-length'] = params.data.length;
    } else {
      params.data = null;
    }
    var urlObj = url.parse(params.url);
    var options = {
      method: params.method,
      host: urlObj.hostname,
      path: urlObj.path,
      port: (urlObj.port ? urlObj.port : (urlObj.protocol=='https:'?443:80)),
      headers: params.headers
    };
    var timer, timedOut;

    if(params.timeout) {
      timer = setTimeout(function() {
        params.error('timeout');
        timedOut=true;
      }, params.timeout);
    }

    var lib = (urlObj.protocol=='https:'?https:http);
    var request = lib.request(options, function(response) {
      var str='';
      response.setEncoding('utf8');
      response.on('data', function(chunk) {
        str+=chunk;
      });
      response.on('end', function() {
        if(timer) {
          clearTimeout(timer);
        }
        if(!timedOut) {
          if(response.statusCode==200 || response.statusCode==201 || response.statusCode==204) {
            params.success(str, normalizeHeaders(response.headers));
          } else {
            params.error(response.statusCode);
          }
        }
      });
    });
    request.on('error', function(e) {
      if(timer) {
        clearTimeout(timer);
      }
      params.error(e.message);
    });
    if(params.data) {
      request.end(params.data);
    } else {
      request.end();
    }
  }

  function parseXmlBrowser(str, cb) {
    var tree=(new DOMParser()).parseFromString(str, 'text/xml');
    var nodes=tree.getElementsByTagName('Link');
    var obj={
      Link: []
    };
    for(var i=0; i<nodes.length; i++) {
      var link={};
      if(nodes[i].attributes) {
        for(var j=0; j<nodes[i].attributes.length;j++) {
          link[nodes[i].attributes[j].name]=nodes[i].attributes[j].value;
        }
      }
      var props = nodes[i].getElementsByTagName('Property');
      link.properties = {};
      for(var k=0; k<props.length;k++) {
        link.properties[
          props[k].getAttribute('type')
        ] = props[k].childNodes[0].nodeValue;
      }
      if(link.rel) {
        obj.Link.push({
          '@': link
        });
      }
    }
    cb(null, obj);
  }

  function parseXmlNode(str, cb) {
    var xml2js=nodeRequire('xml2js');
    new xml2js.Parser().parseString(str, cb);
  }

  function harvestParamNode() {
  }

  function harvestParamBrowser(param) {
    // location.hash in firefox has all URI entities decoded, so we can't
    // differentiate between %26 and & in URIs passed as parameters.
    var hash = String(document.location).split(/^.*?#/)[1];
    if(hash) {
      var pairs = hash.split('&');
      for(var i=0; i<pairs.length; i++) {
        if(pairs[i].substring(0, (param+'=').length) == param+'=') {
          var ret = decodeURIComponent(pairs[i].substring((param+'=').length));
          delete pairs[i];
          document.location = '#'+pairs.join('&');
          return ret;
        }
      }
    }
  }

  function setElementHtmlNode(eltName, html) {
  }

  function setElementHtmlBrowser(eltName, html) {
    var elt = eltName;
    if(! (elt instanceof Element)) {
      elt = document.getElementById(eltName);
    }
    elt.innerHTML = html;
  }

  function getElementValueNode(eltName) {
  }

  function getElementValueBrowser(eltName) {
    return document.getElementById(eltName).value;
  }

  function eltOnNode(eltName, eventType, cb) {
  }

  function eltOnBrowser(eltName, eventType, cb) {
    if(eventType == 'click') {
      document.getElementById(eltName).onclick = cb;
    } else if(eventType == 'hover') {
      document.getElementById(eltName).onmouseover = cb;
    } else if(eventType == 'type') {
      document.getElementById(eltName).onkeyup = cb;
    }
  }

  function getLocationBrowser() {
    //TODO: deal with http://user:a#aa@host.com/ although i doubt someone would actually use that even once between now and the end of the internet
    return window.location.href.split('#')[0];
  }

  function getLocationNode() {
  }

  function setLocationBrowser(location) {
    window.location = location;
  }

  function setLocationNode() {
  }

  function alertBrowser(str) {
    window.alert(str);
  }

  function alertNode(str) {
    console.log(str);
  }

  var inTestMode = false;

  var platform;

  function testMode() {
    platform.ajax = function(params) {
      this.calls.push(params);
      return util.getPromise.fulfillLater(
        this.testResponseText, this.testHeaders
      );
    };

    platform.prepareTest = function(responseText, headers) {
      this.calls = [];
      this.testResponseText = responseText;
      this.testHeaders = headers;
    };
  }

  if(typeof(window) === 'undefined') {
    platform = {
      ajax: ajaxNode,
      parseXml: parseXmlNode,
      harvestParam: harvestParamNode,
      setElementHTML: setElementHtmlNode,
      getElementValue: getElementValueNode,
      eltOn: eltOnNode,
      getLocation: getLocationNode,
      setLocation: setLocationNode,
      alert: alertNode,
      testMode: testMode
    };
  } else {
    if(window.XDomainRequest) {
      platform = {
        ajax: ajaxExplorer,
        parseXml: parseXmlBrowser,
        harvestParam: harvestParamBrowser,
        setElementHTML: setElementHtmlBrowser,
        getElementValue: getElementValueBrowser,
        eltOn: eltOnBrowser,
        getLocation: getLocationBrowser,
        setLocation: setLocationBrowser,
        alert: alertBrowser,
        testMode: testMode
      };
    } else {
      platform = {
        ajax: ajaxBrowser,
        parseXml: parseXmlBrowser,
        harvestParam: harvestParamBrowser,
        setElementHTML: setElementHtmlBrowser,
        getElementValue: getElementValueBrowser,
        eltOn: eltOnBrowser,
        getLocation: getLocationBrowser,
        setLocation: setLocationBrowser,
        alert: alertBrowser,
        testMode: testMode
      };
    }

    return platform;
  }
});
<commit_msg>removed deprecated stuff from platform.js<commit_after>/*global window */
/*global console */
/*global XMLHttpRequest */
/*global XDomainRequest */
/*global Blob */
/*global setTimeout */
/*global clearTimeout */
/*global DOMParser */
/*global Element */
/*global document */

if(typeof(global) !== 'undefined' && typeof(nodeRequire) === 'undefined') {
  var nodeRequire = require;
}

define(['./util'], function(util) {

  "use strict";

  // Namespace: platform
  //
  // Platform specific implementations of common things to do.
  //
  // Method: ajax
  //
  // Set off an HTTP request.
  // Uses CORS, if available on the platform.
  //
  // Parameters:
  //   (given as an *Object*)
  //
  //   url     - URL to send the request to
  //   success - callback function to call when request succeeded
  //   error   - callback function to call when request failed
  //   method  - (optional) HTTP request method to use (default: GET)
  //   headers - (optional) object containing request headers to set
  //   timeout - (optional) milliseconds until request is given up and error
  //             callback is called. If omitted, the request never times out.
  //
  // Example:
  //   (start code)
  //   platform.ajax({
  //     url: "http://en.wikipedia.org/wiki/AJAX",
  //     success: function(responseText, responseHeaders) {
  //       console.log("Here's the page: ", responseText);
  //     },
  //     error: function(errorMessage) {
  //       console.error("Something went wrong: ", errorMessage);
  //     },
  //     timeout: 3000
  //   });
  //   (end code)
  //
  // Platform support:
  //   web browser - YES (if browser <supports CORS at http://caniuse.com/#feat=cors>)
  //   IE - Partially, no support for setting headers.
  //   node - YES, CORS not an issue at all
  //
  //
  // Method: parseXml
  //
  // Parse given XML source.
  //
  // Platform support:
  //   browser - yes, if DOMParser is available
  //   node - yes, if xml2js is available
  //
  //
  // Method: getLocation
  //
  //
  // Method: setLocation
  //

  var logger = util.getLogger('platform');

  // downcase all header keys
  function normalizeHeaders(headers) {
    var h = {};
    for(var key in headers) {
      h[key.toLowerCase()] = headers[key];
    }
    return h;
  }

  function browserParseHeaders(rawHeaders) {
    if(! rawHeaders) {
      // firefox bug. workaround in ajaxBrowser.
      return null;
    }
    var headers = {};
    var lines = rawHeaders.split(/\r?\n/);
    var lastKey = null, md, key, value;
    var numLines = lines.length;
    for(var i=0;i<numLines;i++) {
      if(lines[i].length === 0) {
        // empty line. obviously.
        continue;
      } else if((md = lines[i].match(/^([^:]+):\s*(.+)$/))) {
        // The escaped colon in the following (previously added) comment is
        // necessary, to prevent NaturalDocs from generating a toplevel
        // document called "value line" to the documentation. True story.

        // key\: value line
        key = md[1], value = md[2];
        headers[key] = value;
        lastKey = key;
      } else if((md = lines[i].match(/^\s+(.+)$/))) {
        // continued line (if previous line exceeded 80 bytes
        key = lastKey, value= md[1];
        headers[key] = headers[key] + value;
      } else {
        // nothing we recognize.
        logger.error("Failed to parse header line: " + lines[i]);
      }
    }
    return normalizeHeaders(headers);
  }

  var successStates = { 200:true, 201:true, 204:true, 207:true };

  function isSuccessful(xhr) {
    return !! successStates[xhr.status];
  }

  function ajaxBrowser(params) {
    return util.makePromise(function(promise) {
      var timedOut = false;
      var timer;
      var xhr = new XMLHttpRequest();
      if(params.timeout) {
        timer = window.setTimeout(function() {
          timedOut = true;
          xhr.abort();
          promise.fail('timeout');
        }, params.timeout);
      }

      if(!params.method) {
        params.method = 'GET';
      }

      xhr.open(params.method, params.url, true);

      if(params.headers) {
        for(var header in params.headers) {
          xhr.setRequestHeader(header, params.headers[header]);
        }
      }

      xhr.onreadystatechange = function() {
        if((xhr.readyState == 4)) {
          if(timer) {
            window.clearTimeout(timer);
          }
          if(isSuccessful(xhr)) {
            logger.debug("REQUEST SUCCESSFUL", params.url, xhr.responseText);
            var headers = browserParseHeaders(xhr.getAllResponseHeaders());
            if(! headers) {
              // Firefox' getAllResponseHeaders is broken for CORS requests since forever.
              // https://bugzilla.mozilla.org/show_bug.cgi?id=608735
              // Any additional headers that are needed by other code, should be added here.
              headers = {
                'content-type': xhr.getResponseHeader('Content-Type')
              };
            }
            promise.fulfill(xhr.responseText, headers);
          } else {
            logger.debug("REQUEST FAILED", xhr.status, params.url);
            promise.fail(xhr.status || new Error('network error'));
          }
        }
      };

      if(typeof(params.data) === 'string') {
        xhr.send(params.data);
      } else {
        xhr.send();
      }
    });
  }

  function ajaxExplorer(params) {
    // this won't work, because we have no way of sending
    // the Authorization header. It might work for GET to
    // the 'public' category, though.
    var promise = util.getPromise();
    var xdr = new XDomainRequest();
    xdr.timeout = params.timeout || 3000;//is this milliseconds? documentation doesn't say
    xdr.open(params.method, params.url);
    xdr.onload = function() {
      if(xdr.status == 200 || xdr.status == 201 || xdr.status == 204) {
        promise.fulfill(xdr.responseText);
      } else {
        params.fail(xdr.status);
      }
    };
    xdr.onerror = function() {
      // See http://msdn.microsoft.com/en-us/library/ms536930%28v=vs.85%29.aspx
      promise.fail(new Error('unknown error'));
    };
    xdr.ontimeout = function() {
      promise.fail('timeout');
    };
    if(params.data) {
      xdr.send(params.data);
    } else {
      xdr.send();
    }
  }

  function ajaxNode(params) {

    if(typeof(params.data) === 'object' && params.data instanceof Blob) {
      throw new Error("Sending binary data not yet implemented for nodejs");
    }

    var http=nodeRequire('http'),
      https=nodeRequire('https'),
      url=nodeRequire('url');
    if(!params.method) {
      params.method='GET';
    }
    if(params.data) {
      params.headers['content-length'] = params.data.length;
    } else {
      params.data = null;
    }
    var urlObj = url.parse(params.url);
    var options = {
      method: params.method,
      host: urlObj.hostname,
      path: urlObj.path,
      port: (urlObj.port ? urlObj.port : (urlObj.protocol=='https:'?443:80)),
      headers: params.headers
    };
    var timer, timedOut;

    if(params.timeout) {
      timer = setTimeout(function() {
        params.error('timeout');
        timedOut=true;
      }, params.timeout);
    }

    var lib = (urlObj.protocol=='https:'?https:http);
    var request = lib.request(options, function(response) {
      var str='';
      response.setEncoding('utf8');
      response.on('data', function(chunk) {
        str+=chunk;
      });
      response.on('end', function() {
        if(timer) {
          clearTimeout(timer);
        }
        if(!timedOut) {
          if(response.statusCode==200 || response.statusCode==201 || response.statusCode==204) {
            params.success(str, normalizeHeaders(response.headers));
          } else {
            params.error(response.statusCode);
          }
        }
      });
    });
    request.on('error', function(e) {
      if(timer) {
        clearTimeout(timer);
      }
      params.error(e.message);
    });
    if(params.data) {
      request.end(params.data);
    } else {
      request.end();
    }
  }

  function parseXmlBrowser(str, cb) {
    var tree=(new DOMParser()).parseFromString(str, 'text/xml');
    var nodes=tree.getElementsByTagName('Link');
    var obj={
      Link: []
    };
    for(var i=0; i<nodes.length; i++) {
      var link={};
      if(nodes[i].attributes) {
        for(var j=0; j<nodes[i].attributes.length;j++) {
          link[nodes[i].attributes[j].name]=nodes[i].attributes[j].value;
        }
      }
      var props = nodes[i].getElementsByTagName('Property');
      link.properties = {};
      for(var k=0; k<props.length;k++) {
        link.properties[
          props[k].getAttribute('type')
        ] = props[k].childNodes[0].nodeValue;
      }
      if(link.rel) {
        obj.Link.push({
          '@': link
        });
      }
    }
    cb(null, obj);
  }

  function parseXmlNode(str, cb) {
    var xml2js=nodeRequire('xml2js');
    new xml2js.Parser().parseString(str, cb);
  }

  function harvestParamNode() {
  }

  function harvestParamBrowser(param) {
    // location.hash in firefox has all URI entities decoded, so we can't
    // differentiate between %26 and & in URIs passed as parameters.
    var hash = String(document.location).split(/^.*?#/)[1];
    if(hash) {
      var pairs = hash.split('&');
      for(var i=0; i<pairs.length; i++) {
        if(pairs[i].substring(0, (param+'=').length) == param+'=') {
          var ret = decodeURIComponent(pairs[i].substring((param+'=').length));
          delete pairs[i];
          document.location = '#'+pairs.join('&');
          return ret;
        }
      }
    }
  }

  function getLocationBrowser() {
    //TODO: deal with http://user:a#aa@host.com/ although i doubt someone would actually use that even once between now and the end of the internet
    return window.location.href.split('#')[0];
  }

  function getLocationNode() {
  }

  function setLocationBrowser(location) {
    window.location = location;
  }

  function setLocationNode() {
  }

  var platform;

  if(typeof(window) === 'undefined') {
    platform = {
      ajax: ajaxNode,
      parseXml: parseXmlNode,
      harvestParam: harvestParamNode,
      getLocation: getLocationNode,
      setLocation: setLocationNode
    };
  } else {
    if(window.XDomainRequest) {
      platform = {
        ajax: ajaxExplorer,
        parseXml: parseXmlBrowser,
        harvestParam: harvestParamBrowser,
        getLocation: getLocationBrowser,
        setLocation: setLocationBrowser
      };
    } else {
      platform = {
        ajax: ajaxBrowser,
        parseXml: parseXmlBrowser,
        harvestParam: harvestParamBrowser,
        getLocation: getLocationBrowser,
        setLocation: setLocationBrowser
      };
    }

    return platform;
  }
});
<|endoftext|>


<commit_before>/**
 * # Element visibility toggler.
 *
 * Requires an element to have a data-dough-toggler attribute. The application
 * file will spawn an instance of this class for each element it finds on the page.
 *
 * Events used: toggler:toggled(element, isShown) [Event for when the toggler is doing its work]
 *
 * See test fixture for sample markup - /test/fixtures/VisibilityToggler.html
 */

/**
 * Require from Config
 * @param  {[type]} $         [description]
 * @param  {[type]} DoughBaseComponent [description]
 * @return {[type]}           [description]
 * @private
 */
define(['jquery', 'DoughBaseComponent', 'eventsWithPromises'], function($, DoughBaseComponent, eventsWithPromises) {
  'use strict';

  // Class variables
  var CollapsableProto,
      defaultConfig = {
        hideOnBlur : false,
        forceTo : false
      },
      selectors = {
        activeClass: 'is-active',
        inactiveClass: 'is-inactive',
        iconClassOpen: 'icon--down-chevron-blue',
        iconClassClose: 'icon--up-chevron-blue'
      },
      i18nStrings = {
        open: 'Show',
        close: 'Hide'
      };

  /**
   *
   * @param {jQuery} $el - mandatory - the trigger
   * @param {object} config
   * @returns {Collapsable}
   * @constructor
   */
  function Collapsable($el, config) {
    this.selectors = selectors;
    Collapsable.baseConstructor.call(this, $el, config, defaultConfig);
    this.selectors = $.extend(this.selectors, (config && config.selectors) ? config.selectors : '');
    this.$trigger = this.$el;
    this.$target = $('[data-dough-collapsable-target="' + this.$trigger.attr('data-dough-collapsable-trigger') + '"]');
    this.i18nStrings = (config && config.i18nStrings) ? config.i18nStrings : i18nStrings;
    this._setupAccessibility();
    this.handleUIEventTracking = $.proxy(this.handleUIEventTracking, this);
    config && config.forceTo && this.toggle(config.forceTo, false);
    return this;
  }

  /**
   * Inherit from base module, for shared methods and interface
   * @type {[type]}
   * @private
   */
  DoughBaseComponent.extend(Collapsable);
  CollapsableProto = Collapsable.prototype;

  CollapsableProto._setupAccessibility = function() {
    var id = 'data-dough-collapsable-target-' + this.$target.attr('data-dough-collapsable-target');

    this.$trigger.wrapInner('<button class="unstyled-button" type="button"/>');
    this.$trigger.find('button')
        .prepend('')
        .append('' +
            this.i18nStrings.open + '')
        .attr('aria-controls', id)
        .attr('aria-expanded', 'false');
    this.$target.attr('id', id);
    this.$trigger = this.$trigger.find('button');
  };

  /**
   * Init function
   * @return {Collapsable}
   */
  CollapsableProto.init = function(initialised) {
    // is the target element visible already
    this.isShown = !!this.$target.hasClass(this.selectors.activeClass);
    this.setListeners(true);
    this._initialisedSuccess(initialised);
    return this;
  };

  /**
   * Bind or unbind relevant DOM events
   * @param {Boolean} isActive Set to 'true' to bind to events, 'false' to unbind.
   */
  CollapsableProto.setListeners = function(isActive) {
    this.$trigger[isActive ? 'on' : ' off']('click', $.proxy(function(e) {
      this.toggle();
    }, this));

    return this;
  };

  /**
   * Bind or unbinds the UI Events for tracking the current target
   */
  CollapsableProto.setTargetTrackingListeners = function(isActive) {
    $('body')[isActive ? 'on' : 'off']('click keyup', this.handleUIEventTracking);

    return this;
  };

  /**
   * Handler function for tracking event targets
   * Checks if trigger element is focussed, if the focus is within the target
   * or ESC key is and hides menu accordingly
   * @param  {Object} e The event object for the current target
   * @return {this} This
   */
  CollapsableProto.handleUIEventTracking = function(e) {
    var $currentTarget = $(e.target);
    if(e.keyCode === 27 || !$currentTarget.is(this.$trigger) && !$currentTarget.closest(this.$target).length) {
      this.toggle('hide');
    }

    return this;
  };

  /**
   * Toggle the element
   * @param  {[type]} forceTo Supply 'show' or 'hide' to
   * explicitly set, otherwise will automatically toggle
   * @param {boolean} [focusTarget] - whether to focus the panel
   * @return {[type]}         [description]
   */
  CollapsableProto.toggle = function(forceTo, focusTarget) {
    var func,
        label,
        expandedLabel,
        iconClass;

    // is there an override parameter?
    if (typeof forceTo !== 'undefined') {
      func = forceTo === 'show' ? 'addClass' : 'removeClass';
    } else {
      func = this.isShown ? 'removeClass' : 'addClass';
    }

    // toggle the element
    this.isShown = !!this.$target[func](selectors.activeClass).hasClass(selectors.activeClass);

    // toggle the class on the trigger element (active = shown / nothing = not shown)
    this.$trigger[func](selectors.activeClass);

    if (this.isShown === true) {
      label = this.i18nStrings.close;
      expandedLabel = 'true';
      iconClass = selectors.iconClassClose;
      if (focusTarget !== false) {
        this.$target
            .attr('tabindex', -1)
            .focus();
      }
      if(this.config.hideOnBlur) {
        this.setTargetTrackingListeners(true);
      }
    } else {
      label = this.i18nStrings.open;
      expandedLabel = 'false';
      iconClass = selectors.iconClassOpen;
      if(this.config.hideOnBlur) {
        this.setTargetTrackingListeners(false);
      }
    }

    this.$trigger.find('[data-dough-collapsable-label]').text(label);
    this.$trigger.attr('aria-expanded', expandedLabel);
    this.$trigger
        .find('[data-dough-collapsable-icon]')
        .removeClass(selectors.iconClassOpen + ' ' + selectors.iconClassClose).addClass(iconClass);

    // can bind to this by eventsWithPromises.subscribe('toggler:toggled', function(Collapsable) { });
    if (typeof forceTo === 'undefined') {
      eventsWithPromises.publish('toggler:toggled', {
        emitter: this,
        visible: this.isShown
      });
    }

    return this;
  };

  return Collapsable;
});
<commit_msg>Remove hide on ESC key functionality form Collapsable<commit_after>/**
 * # Element visibility toggler.
 *
 * Requires an element to have a data-dough-toggler attribute. The application
 * file will spawn an instance of this class for each element it finds on the page.
 *
 * Events used: toggler:toggled(element, isShown) [Event for when the toggler is doing its work]
 *
 * See test fixture for sample markup - /test/fixtures/VisibilityToggler.html
 */

/**
 * Require from Config
 * @param  {[type]} $         [description]
 * @param  {[type]} DoughBaseComponent [description]
 * @return {[type]}           [description]
 * @private
 */
define(['jquery', 'DoughBaseComponent', 'eventsWithPromises'], function($, DoughBaseComponent, eventsWithPromises) {
  'use strict';

  // Class variables
  var CollapsableProto,
      defaultConfig = {
        hideOnBlur : false,
        forceTo : false
      },
      selectors = {
        activeClass: 'is-active',
        inactiveClass: 'is-inactive',
        iconClassOpen: 'icon--down-chevron-blue',
        iconClassClose: 'icon--up-chevron-blue'
      },
      i18nStrings = {
        open: 'Show',
        close: 'Hide'
      };

  /**
   *
   * @param {jQuery} $el - mandatory - the trigger
   * @param {object} config
   * @returns {Collapsable}
   * @constructor
   */
  function Collapsable($el, config) {
    this.selectors = selectors;
    Collapsable.baseConstructor.call(this, $el, config, defaultConfig);
    this.selectors = $.extend(this.selectors, (config && config.selectors) ? config.selectors : '');
    this.$trigger = this.$el;
    this.$target = $('[data-dough-collapsable-target="' + this.$trigger.attr('data-dough-collapsable-trigger') + '"]');
    this.i18nStrings = (config && config.i18nStrings) ? config.i18nStrings : i18nStrings;
    this._setupAccessibility();
    this.handleUIEventTracking = $.proxy(this.handleUIEventTracking, this);
    config && config.forceTo && this.toggle(config.forceTo, false);
    return this;
  }

  /**
   * Inherit from base module, for shared methods and interface
   * @type {[type]}
   * @private
   */
  DoughBaseComponent.extend(Collapsable);
  CollapsableProto = Collapsable.prototype;

  CollapsableProto._setupAccessibility = function() {
    var id = 'data-dough-collapsable-target-' + this.$target.attr('data-dough-collapsable-target');

    this.$trigger.wrapInner('<button class="unstyled-button" type="button"/>');
    this.$trigger.find('button')
        .prepend('')
        .append('' +
            this.i18nStrings.open + '')
        .attr('aria-controls', id)
        .attr('aria-expanded', 'false');
    this.$target.attr('id', id);
    this.$trigger = this.$trigger.find('button');
  };

  /**
   * Init function
   * @return {Collapsable}
   */
  CollapsableProto.init = function(initialised) {
    // is the target element visible already
    this.isShown = !!this.$target.hasClass(this.selectors.activeClass);
    this.setListeners(true);
    this._initialisedSuccess(initialised);
    return this;
  };

  /**
   * Bind or unbind relevant DOM events
   * @param {Boolean} isActive Set to 'true' to bind to events, 'false' to unbind.
   */
  CollapsableProto.setListeners = function(isActive) {
    this.$trigger[isActive ? 'on' : ' off']('click', $.proxy(function(e) {
      this.toggle();
    }, this));

    return this;
  };

  /**
   * Bind or unbinds the UI Events for tracking the current target
   */
  CollapsableProto.setTargetTrackingListeners = function(isActive) {
    $('body')[isActive ? 'on' : 'off']('click keyup', this.handleUIEventTracking);

    return this;
  };

  /**
   * Handler function for tracking event targets
   * Checks if trigger element is focussed, if the focus is within the target
   * @param  {Object} e The event object for the current target
   * @return {this} This
   */
  CollapsableProto.handleUIEventTracking = function(e) {
    var $currentTarget = $(e.target);
    if(!$currentTarget.is(this.$trigger) && !$currentTarget.closest(this.$target).length) {
      this.toggle('hide');
    }

    return this;
  };

  /**
   * Toggle the element
   * @param  {[type]} forceTo Supply 'show' or 'hide' to
   * explicitly set, otherwise will automatically toggle
   * @param {boolean} [focusTarget] - whether to focus the panel
   * @return {[type]}         [description]
   */
  CollapsableProto.toggle = function(forceTo, focusTarget) {
    var func,
        label,
        expandedLabel,
        iconClass;

    // is there an override parameter?
    if (typeof forceTo !== 'undefined') {
      func = forceTo === 'show' ? 'addClass' : 'removeClass';
    } else {
      func = this.isShown ? 'removeClass' : 'addClass';
    }

    // toggle the element
    this.isShown = !!this.$target[func](selectors.activeClass).hasClass(selectors.activeClass);

    // toggle the class on the trigger element (active = shown / nothing = not shown)
    this.$trigger[func](selectors.activeClass);

    if (this.isShown === true) {
      label = this.i18nStrings.close;
      expandedLabel = 'true';
      iconClass = selectors.iconClassClose;
      if (focusTarget !== false) {
        this.$target
            .attr('tabindex', -1)
            .focus();
      }
      if(this.config.hideOnBlur) {
        this.setTargetTrackingListeners(true);
      }
    } else {
      label = this.i18nStrings.open;
      expandedLabel = 'false';
      iconClass = selectors.iconClassOpen;
      if(this.config.hideOnBlur) {
        this.setTargetTrackingListeners(false);
      }
    }

    this.$trigger.find('[data-dough-collapsable-label]').text(label);
    this.$trigger.attr('aria-expanded', expandedLabel);
    this.$trigger
        .find('[data-dough-collapsable-icon]')
        .removeClass(selectors.iconClassOpen + ' ' + selectors.iconClassClose).addClass(iconClass);

    // can bind to this by eventsWithPromises.subscribe('toggler:toggled', function(Collapsable) { });
    if (typeof forceTo === 'undefined') {
      eventsWithPromises.publish('toggler:toggled', {
        emitter: this,
        visible: this.isShown
      });
    }

    return this;
  };

  return Collapsable;
});
<|endoftext|>


<commit_before>
var stores = require('./stores');

function Blockchain(block) {
    var self = this;
    var blocks = [block];
    var blockstore = stores.blockstore();
    blockstore.save(block);

    this.bestBlock = function () { return blocks[blocks.length - 1]; }

    this.add = function (block) {
        if (blockstore.hasBlockHash(block.hash))
            return;

        blockstore.save(block);

        tryAdd(block);
    }

    function tryAdd(block) {
        if (isBestBlockChild(block))
            blocks.push(block);

        if (isBetterBestBlock(block))
            tryFork(block);

        tryChildren(block);
    }

    function tryChildren(block) {
        var children = blockstore.getChildren(block.hash);

        for (var n in children)
            tryAdd(children[n]);
    }

    function isBestBlockChild(block) {
        var bblock = self.bestBlock();

        return bblock.hash === block.parentHash && bblock.number === block.number - 1;
    }

    function isBetterBestBlock(block) {
        var bblock = self.bestBlock();

        return bblock.number < block.number;
    }

    function tryFork(block) {
        var newbranch = [block];
        var parentHash = block.parentHash;

        while (parentHash && blockstore.hasBlockHash(parentHash)) {
            var parent = blockstore.getByHash(parentHash);

            if (parent.hash === blocks[parent.number].hash)
                return switchToFork(newbranch);

            newbranch.push(parent);

            parentHash = parent.parentHash;
        }
    }

    function getDescendants(block) {
        var children = getChildren(block);

        if (children.length == 0)
            return children;

        var descendants = []

        for (var n in children) {
            var child = children[n];
            descendants = descendants.concat(getChildren(child));
        }
    }

    function switchToFork(newbranch) {
        for (var n = newbranch.length; n-- > 0;) {
            var block = newbranch[n];
            blocks[block.number] = block;
        }
    }
}

function createBlockchain(block) {
    return new Blockchain(block);
}

module.exports = {
    blockchain: createBlockchain
}

<commit_msg>Blockchain add internal refactor<commit_after>
var stores = require('./stores');

function Blockchain(block) {
    var self = this;
    var blocks = [block];
    var blockstore = stores.blockstore();
    blockstore.save(block);

    this.bestBlock = function () { return blocks[blocks.length - 1]; }

    this.add = function (block) {
        if (blockstore.hasBlockHash(block.hash))
            return;

        blockstore.save(block);

        if (getUnknownAncestor(block) != null)
            return;

        tryAdd(block);
    }

    function tryAdd(block) {
        if (isBestBlockChild(block))
            blocks.push(block);

        if (isBetterBestBlock(block))
            tryFork(block);

        tryChildren(block);
    }

    function tryChildren(block) {
        var children = blockstore.getChildren(block.hash);

        for (var n in children)
            tryAdd(children[n]);
    }

    function isBestBlockChild(block) {
        var bblock = self.bestBlock();

        return bblock.hash === block.parentHash && bblock.number === block.number - 1;
    }

    function isBetterBestBlock(block) {
        var bblock = self.bestBlock();

        return bblock.number < block.number;
    }

    function tryFork(block) {
        var newbranch = [block];
        var parentHash = block.parentHash;

        while (parentHash && blockstore.hasBlockHash(parentHash)) {
            var parent = blockstore.getByHash(parentHash);

            if (parent.hash === blocks[parent.number].hash)
                return switchToFork(newbranch);

            newbranch.push(parent);

            parentHash = parent.parentHash;
        }
    }

    function getUnknownAncestor(block) {
        var parentHash = block.parentHash;

        while (parentHash && blockstore.hasBlockHash(parentHash)) {
            var parent = blockstore.getByHash(parentHash);

            if (parent.hash === blocks[parent.number].hash)
                return null;

            parentHash = parent.parentHash;
        }

        return parentHash;
    }

    function getDescendants(block) {
        var children = getChildren(block);

        if (children.length == 0)
            return children;

        var descendants = []

        for (var n in children) {
            var child = children[n];
            descendants = descendants.concat(getChildren(child));
        }
    }

    function switchToFork(newbranch) {
        for (var n = newbranch.length; n-- > 0;) {
            var block = newbranch[n];
            blocks[block.number] = block;
        }
    }
}

function createBlockchain(block) {
    return new Blockchain(block);
}

module.exports = {
    blockchain: createBlockchain
}

<|endoftext|>


<commit_before>
// Raw
// -------
var inherits      = require('inherits')
var EventEmitter  = require('events').EventEmitter
var assign        = require('lodash/object/assign')
var reduce        = require('lodash/collection/reduce')
var isPlainObject = require('lodash/lang/isPlainObject')

function Raw(client) {
  this.client   = client

  this.sql      = ''
  this.bindings = []
  this._cached  = undefined

  // Todo: Deprecate
  this._wrappedBefore = undefined
  this._wrappedAfter  = undefined
  this._debug         = client && client.options && client.options.debug
}
inherits(Raw, EventEmitter)

assign(Raw.prototype, {

  set: function(sql, bindings) {
    this._cached  = undefined
    this.sql      = sql
    this.bindings = bindings
    return this
  },

  // Wraps the current sql with `before` and `after`.
  wrap: function(before, after) {
    this._cached        = undefined
    this._wrappedBefore = before
    this._wrappedAfter  = after
    return this
  },

  // Calls `toString` on the Knex object.
  toString: function() {
    return this.toQuery()
  },

  // Returns the raw sql for the query.
  toSQL: function() {
    if (this._cached) return this._cached
    if (Array.isArray(this.bindings)) {
      this._cached = replaceRawArrBindings(this)
    } else if (this.bindings && isPlainObject(this.bindings)) {
      this._cached = replaceKeyBindings(this)
    } else {
      this._cached = {
        method: 'raw',
        sql: this.sql,
        bindings: this.bindings
      }
    }
    if (this._wrappedBefore) {
      this._cached.sql = this._wrappedBefore + this._cached.sql
    }
    if (this._wrappedAfter) {
      this._cached.sql = this._cached.sql + this._wrappedAfter
    }
    this._cached.options = reduce(this._options, assign, {})
    return this._cached
  }

})

function replaceRawArrBindings(raw) {
  var expectedBindings = raw.bindings.length
  var values           = raw.bindings
  var client           = raw.client
  var index            = 0;
  var bindings         = []

  var sql = raw.sql.replace(/\?\??/g, function(match) {
    var value = values[index++]

    if (value && typeof value.toSQL === 'function') {
      var bindingSQL = value.toSQL()
      if (bindingSQL.bindings !== undefined) {
        bindings = bindings.concat(bindingSQL.bindings)
      }
      return bindingSQL.sql
    }

    if (match === '??') {
      return client.formatter().columnize(value)
    }
    bindings.push(value)
    return '?'
  })

  if (expectedBindings !== index) {
    throw new Error('Expected ' + expectedBindings + ' bindings, saw ' + index)
  }

  return {
    method: 'raw',
    sql: sql,
    bindings: bindings
  }
}

function replaceKeyBindings(raw) {
  var values   = raw.bindings
  var client   = raw.client
  var sql      = raw.sql, bindings = []

  var regex = new RegExp('(^|\\s)(\\:\\w+\\:?)', 'g')
  sql = raw.sql.replace(regex, function(full) {
    var key = full.trim();
    var isIdentifier = key[key.length - 1] === ':'
    var value = isIdentifier ? values[key.slice(1, -1)] : values[key.slice(1)]
    if (value === undefined) return ''
    if (value && typeof value.toSQL === 'function') {
      var bindingSQL = value.toSQL()
      if (bindingSQL.bindings !== undefined) {
        bindings = bindings.concat(bindingSQL.bindings)
      }
      return full.replace(key, bindingSQL.sql)
    }
    if (isIdentifier) {
      return full.replace(key, client.formatter().columnize(value))
    }
    bindings.push(value)
    return full.replace(key, '?')
  })

  return {
    method: 'raw',
    sql: sql,
    bindings: bindings
  }
}

// Allow the `Raw` object to be utilized with full access to the relevant
// promise API.
require('./interface')(Raw)

module.exports = Raw
<commit_msg>Prevent \? from getting recognized when collecting array bindings<commit_after>
// Raw
// -------
var inherits      = require('inherits')
var EventEmitter  = require('events').EventEmitter
var assign        = require('lodash/object/assign')
var reduce        = require('lodash/collection/reduce')
var isPlainObject = require('lodash/lang/isPlainObject')

function Raw(client) {
  this.client   = client

  this.sql      = ''
  this.bindings = []
  this._cached  = undefined

  // Todo: Deprecate
  this._wrappedBefore = undefined
  this._wrappedAfter  = undefined
  this._debug         = client && client.options && client.options.debug
}
inherits(Raw, EventEmitter)

assign(Raw.prototype, {

  set: function(sql, bindings) {
    this._cached  = undefined
    this.sql      = sql
    this.bindings = bindings
    return this
  },

  // Wraps the current sql with `before` and `after`.
  wrap: function(before, after) {
    this._cached        = undefined
    this._wrappedBefore = before
    this._wrappedAfter  = after
    return this
  },

  // Calls `toString` on the Knex object.
  toString: function() {
    return this.toQuery()
  },

  // Returns the raw sql for the query.
  toSQL: function() {
    if (this._cached) return this._cached
    if (Array.isArray(this.bindings)) {
      this._cached = replaceRawArrBindings(this)
    } else if (this.bindings && isPlainObject(this.bindings)) {
      this._cached = replaceKeyBindings(this)
    } else {
      this._cached = {
        method: 'raw',
        sql: this.sql,
        bindings: this.bindings
      }
    }
    if (this._wrappedBefore) {
      this._cached.sql = this._wrappedBefore + this._cached.sql
    }
    if (this._wrappedAfter) {
      this._cached.sql = this._cached.sql + this._wrappedAfter
    }
    this._cached.options = reduce(this._options, assign, {})
    return this._cached
  }

})

function replaceRawArrBindings(raw) {
  var expectedBindings = raw.bindings.length
  var values           = raw.bindings
  var client           = raw.client
  var index            = 0;
  var bindings         = []

  var sql = raw.sql.replace(/\\?\?\??/g, function(match) {
    if (match === '\\?') {
      return match
    }

    var value = values[index++]

    if (value && typeof value.toSQL === 'function') {
      var bindingSQL = value.toSQL()
      if (bindingSQL.bindings !== undefined) {
        bindings = bindings.concat(bindingSQL.bindings)
      }
      return bindingSQL.sql
    }

    if (match === '??') {
      return client.formatter().columnize(value)
    }
    bindings.push(value)
    return '?'
  })

  if (expectedBindings !== index) {
    throw new Error('Expected ' + expectedBindings + ' bindings, saw ' + index)
  }

  return {
    method: 'raw',
    sql: sql,
    bindings: bindings
  }
}

function replaceKeyBindings(raw) {
  var values   = raw.bindings
  var client   = raw.client
  var sql      = raw.sql, bindings = []

  var regex = new RegExp('(^|\\s)(\\:\\w+\\:?)', 'g')
  sql = raw.sql.replace(regex, function(full) {
    var key = full.trim();
    var isIdentifier = key[key.length - 1] === ':'
    var value = isIdentifier ? values[key.slice(1, -1)] : values[key.slice(1)]
    if (value === undefined) return ''
    if (value && typeof value.toSQL === 'function') {
      var bindingSQL = value.toSQL()
      if (bindingSQL.bindings !== undefined) {
        bindings = bindings.concat(bindingSQL.bindings)
      }
      return full.replace(key, bindingSQL.sql)
    }
    if (isIdentifier) {
      return full.replace(key, client.formatter().columnize(value))
    }
    bindings.push(value)
    return full.replace(key, '?')
  })

  return {
    method: 'raw',
    sql: sql,
    bindings: bindings
  }
}

// Allow the `Raw` object to be utilized with full access to the relevant
// promise API.
require('./interface')(Raw)

module.exports = Raw
<|endoftext|>


<commit_before>
var baseURL = "http://www.myapp.com/";
var setPostPhotoURL = "http://smurf.grep-r.com/photos/upload";
Titanium.App.Properties.setString("baseURL",baseURL);
Titanium.App.Properties.setString("postPhotoURL",setPostPhotoURL);
<commit_msg>Changed properties file.  Changed to new image post location.<commit_after>
var baseURL = "http://www.myapp.com/";
var setPostPhotoURL = "http://smurf.grep-r.com/photosguests/upload";
Titanium.App.Properties.setString("baseURL",baseURL);
Titanium.App.Properties.setString("postPhotoURL",setPostPhotoURL);
<|endoftext|>