<commit_before>import btoa from 'btoa';
import invariant from 'invariant';
import transformDataToAsset from './transformDataToAsset';
import {getUrlsForV1Server} from './V1Server';

const createHeaderObj = authentication => {
	const headerObj = {
		Accept: 'application/json',
		'Content-Type': 'application/json'
	};
	if (authentication) {
		headerObj.Authorization = authentication;
	}
	return headerObj;
};

export default class V1Meta {
	constructor({hostname, instance, protocol, port, username, password, postFn, getFn}) {
		this.urls = getUrlsForV1Server({hostname, instance, protocol, port});
		this.postFn = postFn;
		this.getFn = getFn;
		if (username && password) {
			const encodedAuthenticationCredentials = btoa(`${username}:${password}`);
			this.authHeader = `Basic ${encodedAuthenticationCredentials}`;
		}
	}

	create(assetType, assetAttributeData) {
		const postData = transformDataToAsset(assetAttributeData);
		const url = `${this.urls.rest()}/${assetType}`;
		const headers = createHeaderObj(this.authHeader);
		return Promise.resolve(this.postFn(url, postData, headers));
	}

	update(oidToken, assetType, assetData, changeComment) {
		const postData = transformDataToAsset(assetData);
		const url = `${this.urls.rest()}/${assetType}/${oidToken}` + (changeComment ? `?comment=${encodeURIComponent(changeComment)}` : '');
		const headers = createHeaderObj(this.authHeader);
		return Promise.resolve(this.postFn(url, postData, headers));
	}

	query(queryObj) {
		invariant(queryObj.from, `Error: there was no \`from\` property on provided query: ${queryObj}`);
		invariant(queryObj.select, `Error: there was no \`from\` property on provided query: ${queryObj}`);
		invariant(Array.isArray(queryObj.select), `Error: there was no \`from\` property on provided query: ${queryObj}`);
		const url = this.urls.query();
		return this.getFn(url, queryObj);
	}
}
<commit_msg>#25 Queries should utilize a POST mechanism and not a GET mechanism. It also requires authorization headers to be included in the request.<commit_after>import btoa from 'btoa';
import invariant from 'invariant';
import transformDataToAsset from './transformDataToAsset';
import {getUrlsForV1Server} from './V1Server';

const createHeaderObj = authentication => {
	const headerObj = {
		Accept: 'application/json',
		'Content-Type': 'application/json'
	};
	if (authentication) {
		headerObj.Authorization = authentication;
	}
	return headerObj;
};

export default class V1Meta {
	constructor({hostname, instance, protocol, port, username, password, postFn, getFn}) {
		this.urls = getUrlsForV1Server({hostname, instance, protocol, port});
		this.postFn = postFn;
		this.getFn = getFn;
		if (username && password) {
			const encodedAuthenticationCredentials = btoa(`${username}:${password}`);
			this.authHeader = `Basic ${encodedAuthenticationCredentials}`;
		}
	}

	create(assetType, assetAttributeData) {
		const postData = transformDataToAsset(assetAttributeData);
		const url = `${this.urls.rest()}/${assetType}`;
		const headers = createHeaderObj(this.authHeader);
		return Promise.resolve(this.postFn(url, postData, headers));
	}

	update(oidToken, assetType, assetData, changeComment) {
		const postData = transformDataToAsset(assetData);
		const url = `${this.urls.rest()}/${assetType}/${oidToken}` + (changeComment ? `?comment=${encodeURIComponent(changeComment)}` : '');
		const headers = createHeaderObj(this.authHeader);
		return Promise.resolve(this.postFn(url, postData, headers));
	}

	query(queryObj) {
		invariant(queryObj.from, `Error: there was no \`from\` property on provided query: ${queryObj}`);
		invariant(queryObj.select, `Error: there was no \`select\` property on provided query: ${queryObj}`);
		invariant(Array.isArray(queryObj.select), `Error: \`select\` property must be an Array on provided query: ${queryObj}`);
		const url = this.urls.query();
		const headers = createHeaderObj(this.authHeader);
		return this.postFn(url, queryObj, headers);
	}
}
<|endoftext|>


<commit_before>import React, { useState, useEffect } from 'react';
import indent from 'indent';
import clipboard from 'clipboard-js';
import classnames from 'classnames';
import Toolbar from '@material-ui/core/Toolbar';
import Button from '@material-ui/core/Button';
import Snackbar from '@material-ui/core/Snackbar';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
import SyntaxHighlighter from 'react-syntax-highlighter';

export default function Viewer({ className, jsonld, async, ...props }) {
  const [showNotification, setShowNotification] = useState(false);
  const [minified, setMinified] = useState(false);
  const [markup, setMarkup] = useState(renderMarkup(jsonld));

  function renderMarkup(jsonld = [], minified = false) {
    if (jsonld.length > 0) {
      var joinChar = minified ? '' : '\n';
      var indentation = minified ? 0 : 2;
      return jsonld
        .map(object => {
          return [
            ''
          ].join(joinChar);
        })
        .join(joinChar);
    } else {
      return '';
    }
  }

  useEffect(() => {
    const markup = renderMarkup(jsonld, minified);
    setMarkup(markup);
  }, [jsonld, minified]);

  async function copyToClipboard() {
    await clipboard.copy(markup);
    setShowNotification(true);
  }

  function closeNotification() {
    setShowNotification(false);
  }

  const clipboardEnabled = jsonld.length > 0;

  return (

      <Toolbar disableGutters>

        <FormControlLabel
          label="Minified"
          control={
            <Switch
              value={minified}
              onChange={e => setMinified(e.target.checked)}
            />
          }
        />
        <Button
          variant="outlined"
          onClick={copyToClipboard}
          disabled={!clipboardEnabled}
        >
          Copy to clipboard
        </Button>
      </Toolbar>
      <SyntaxHighlighter language="markup">{markup}</SyntaxHighlighter>
      <Snackbar
        action={
          <IconButton color="inherit" onClick={closeNotification}>
            <CloseIcon />
          </IconButton>
        }
        open={showNotification}
        autoHideDuration={3000}
        onClose={closeNotification}
        message="Your snippet has been copied to the clipboard."
      />

  );
}
<commit_msg>vdgffg<commit_after>import React, { useState, useEffect } from 'react';
import indent from 'indent';
import clipboard from 'clipboard-js';
import classnames from 'classnames';
import Toolbar from '@material-ui/core/Toolbar';
import Button from '@material-ui/core/Button';
import Snackbar from '@material-ui/core/Snackbar';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import markup from 'react-syntax-highlighter/dist/cjs/languages/prism/markup';
import prism from 'react-syntax-highlighter/dist/cjs/styles/prism/prism';
SyntaxHighlighter.registerLanguage('markup', markup);

export default function Viewer({ className, jsonld, async, ...props }) {
  const [showNotification, setShowNotification] = useState(false);
  const [minified, setMinified] = useState(false);
  const [markup, setMarkup] = useState(renderMarkup(jsonld));

  function renderMarkup(jsonld = [], minified = false) {
    if (jsonld.length > 0) {
      var joinChar = minified ? '' : '\n';
      var indentation = minified ? 0 : 2;
      return jsonld
        .map(object => {
          return [
            ''
          ].join(joinChar);
        })
        .join(joinChar);
    } else {
      return '';
    }
  }

  useEffect(() => {
    const markup = renderMarkup(jsonld, minified);
    setMarkup(markup);
  }, [jsonld, minified]);

  async function copyToClipboard() {
    await clipboard.copy(markup);
    setShowNotification(true);
  }

  function closeNotification() {
    setShowNotification(false);
  }

  const clipboardEnabled = jsonld.length > 0;

  return (

      <Toolbar disableGutters>

        <FormControlLabel
          label="Minified"
          control={
            <Switch
              value={minified}
              onChange={e => setMinified(e.target.checked)}
            />
          }
        />
        <Button
          variant="outlined"
          onClick={copyToClipboard}
          disabled={!clipboardEnabled}
        >
          Copy to clipboard
        </Button>
      </Toolbar>
      <SyntaxHighlighter language="markup" style={prism}>
        {markup}
      </SyntaxHighlighter>
      <Snackbar
        action={
          <IconButton color="inherit" onClick={closeNotification}>
            <CloseIcon />
          </IconButton>
        }
        open={showNotification}
        autoHideDuration={3000}
        onClose={closeNotification}
        message="Your snippet has been copied to the clipboard."
      />

  );
}
<|endoftext|>


<commit_before>/* jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/* global define, $, brackets, setTimeout */

// this function's purpose is to make CodeMirror's vim keymap play nice with
// Brackets.
define(function (require, exports) {
    "use strict";

    var CodeMirror      = brackets.getModule("thirdparty/CodeMirror2/lib/codemirror"),
        CommandManager  = brackets.getModule("command/CommandManager"),
        DocumentManager = brackets.getModule("document/DocumentManager"),
        EditorManager   = brackets.getModule("editor/EditorManager"),
        Commands        = brackets.getModule("command/Commands"),
        CommandDialog,
        cm,
        fecm,
        precm,
        vimMode,
        controller,
        commandDone;

    /**
     * Initialize Vim fixes for CodeMirror -> Brackets integration.
     * @param {CodeMirror} _cm Current CodeMirror instance.
     * @param {CommandDialog} _CommandDialog Current CommandDialog instance.
     * @param {Object} _controller Vim enable/disable controller functions.
     */
    function init(_cm, _CommandDialog, _controller) {
        CommandDialog = _CommandDialog;
        cm = _cm;
        controller = _controller;

        // CodeMirror -> Brackets Command Hooks
        CodeMirror.commands.save = function () {
            CommandManager.execute("file.save");
        };
        CodeMirror.commands.close = function () {
            var hostEditor = EditorManager.getCurrentFullEditor(),
                inlineFocused = EditorManager.getFocusedInlineWidget();
            if (inlineFocused) {
                // inline editor exists & is in focus. close it.
                EditorManager.closeInlineWidget(hostEditor, inlineFocused);
            } else {
                // no inline editor is in focus so just close the document.
                CommandManager.execute("file.close");
            }
        };
        CodeMirror.commands.open = function () {
            // used to be "file.open" because quickOpen would automatically close
            // following the user's push of Enter key to submit Open command (":e[Enter]")
            // setTimeout meant to give the user a moment to let go of the Enter key.
            setTimeout(function () {
                CommandManager.execute("navigate.quickOpen");
            }, 150);
        };

        // init inline editor fix
        $(EditorManager).on("activeEditorChange", changeEditor);
        // add event listener in case user decides to disable vimderbar.
        $(EditorManager).on("vimderbarDisabled", function () {
            $(EditorManager).off("activeEditorChange", changeEditor);
        });

        // Ex Command Definitions
        CodeMirror.Vim.defineEx("quit", "q", function (cm) {
            cm.focus();
            CodeMirror.commands.close(cm);
        });
        CodeMirror.Vim.defineEx("edit", "e", function (cm) {
            CodeMirror.commands.open(cm);
        });
        CodeMirror.Vim.defineEx("write", "w", function (cm) {
            CodeMirror.commands.save(cm);
        });
        CodeMirror.Vim.defineEx("bnext", "bn", function (cm) {
            var file = DocumentManager.getNextPrevFile(1);
            CommandManager.execute(Commands.FILE_OPEN, { fullPath: file.fullPath });
            cm.focus();
        });
        CodeMirror.Vim.defineEx("bprev", "bp", function (cm) {
            var file = DocumentManager.getNextPrevFile(-1);
            CommandManager.execute(Commands.FILE_OPEN, { fullPath: file.fullPath });
            cm.focus();
        });
        CodeMirror.Vim.defineEx("clearhistory", "clearhistory", function (cm) {
            CommandDialog.resetHistory();
            cm.focus();
        });
    }
    /**
     * @private
     * Handler for CodeMirror's vim.js vim-keypress event. Filters and updates keys, clears commands when next command input.
     * @param {String} key Key pressed.
     */
    function _onKeypress(key) {
        // fix broken '/' search by calling cmd.find
        if (key === '/') { CommandManager.execute("cmd.find"); }
        // prevent esc from being displayed or clearing the dialog keys
        if (key.match(/Esc/)) { return; }
        if (!key.match(/:|h|j|k|l|;/)) {
            if (commandDone) {
                cm.clearVimCommandKeys();
                commandDone = false;
            }
            cm.updateVimCommandKeys(key);
        }
    }
    /**
     * @private
     * Handler for CodeMirror's vim.js vim-command-done event. Sets commandDone so current command is cleared on next keypress.
     */
    function _onCommandDone() {
        commandDone = true;
    }
    /**
     * Attach events to current CodeMirror instance.
     * @param {CodeMirror} cm Current CodeMirror instance.
     */
    function attachVimderbar(cm) {
        cm.off("vim-keypress", _onKeypress);
        cm.on("vim-keypress", _onKeypress);
        cm.off("vim-command-done", _onCommandDone);
        cm.on("vim-command-done", _onCommandDone);
    }
    /**
     * Change current document, attach events to new editor.
     * @param {CodeMirror} cm CodeMirror instance to attach to.
     */
    function changeDocument(cm) {
        attachVimderbar(cm);
    }
    // TODO: fix insert cursor in inline editors.
    //    (open inline editor in normal mode. press i from within the inline editor.
    //     notice that the cursor remains "fat".)
    //    fixing this bug will likely fix the visual mode bug described below.

    // TODO: solve the inline-editor dilemma
    /**
     * @private
     * Close inline editor when esc is pressed outside of insert/replace mode.
     * @param {CodeMirror} cm Current CodeMirror instance. Unused.
     * @param {jQuery.Event} e KeyEvent event object.
     */
    function _escKeyEvent(cm, e) {
        if (e.keyCode === 27) {
            CodeMirror.e_stop(e);

            vimMode = fecm.getOption("keyMap");

            if (fecm.state.vim) {
                if (fecm.state.vim && fecm.state.vim.visualMode) {
                    CodeMirror.keyMap.vim.Esc(fecm);
                } else if (vimMode === "vim-insert" || vimMode === "vim-replace") {
                    CodeMirror.keyMap["vim-insert"].Esc(fecm);
                } else {
                    CodeMirror.commands.close(fecm);
                }
            }
        }
    }
    /**
     * Set CodeMirror KeyMap based on mode changes.
     * @param {String} map New map name.
     */
    function setVimKeyMap(map) {
        var mode;
        fecm.setOption("keyMap", map);
        switch (map) {
        case "vim-insert":
            mode = "insert";
            break;
        case "vim-replace":
            fecm.toggleOverwrite(true);
            mode = "replace";
            break;
        default:
            mode = "normal";
        }
        CodeMirror.signal(fecm, "vim-mode-change", {
            mode: mode
        });
    }
    /**
     * Change editor, possibly to inline editor.
     * @param {jQuery.Event} event Active editor change event.
     * @param {Editor} focusedEditor Currently focused editor.
     * @param {Editor} previousEditor Editor that last had focus.
     */
    // TODO: Inline editing reverts to non-vim keymap.
    function changeEditor(event, focusedEditor, previousEditor) {
        if (focusedEditor && previousEditor) {
            // make sure focusedEditor and previousEditor exist.
            // this check is to make sure no errors are thrown when
            // a file is opened.
            fecm = focusedEditor._codeMirror;
            precm = previousEditor._codeMirror;

            // get editors so we don't waste resources when user swaps between
            // documents. Only watch if the focused editor is an inline editor
            // or if the focused editor contains inline editors.
            var currentFullEditor = EditorManager.getCurrentFullEditor(),
                inlineEditors = currentFullEditor.getInlineWidgets();
            // ._visibleRange still the only reliable way to detect whether an
            // editor is inline or not. I'm having a hard time thinking of another
            // way to do this.
            if (focusedEditor._visibleRange || inlineEditors.length !== 0) {
                var feKeyMap = fecm.getOption("keyMap"),
                    preKeyMap = precm.getOption("keyMap");

                // if the keymap isn't one of Vim.js' keymap options, turn the vim on
                // and set it to the proper mode.
                if (feKeyMap !== "vim" && feKeyMap !== "vim-insert" && feKeyMap !== "vim-replace") {
                    // enable vim
                    controller.enable(focusedEditor._codeMirror);
                    // force an esc on the editor to get it to act
                    // normally after change.
                    CodeMirror.keyMap.vim.Esc(fecm);
                    // set vim option
                    setVimKeyMap(preKeyMap);
                    attachVimderbar(fecm);
                }

                if (precm.state.vim && precm.state.vim.visualMode) {
                    // previous state was "visual" so exit whatever mode currently in
                    // and set to visual
                    if (feKeyMap === "vim-insert" || feKeyMap === "vim-replace") {
                        // exit whatever mode currently in.
                        CodeMirror.keyMap["vim-insert"].Esc(fecm);
                    } else {
                        CodeMirror.keyMap.vim.Esc(fecm);
                    }

                    if (preKeyMap === "vim-insert" || preKeyMap === "vim-replace") {
                        // from the user's perspective, document's vim is in
                        // "insert" or "replace" but the code claims document vim was at
                        // some point in visual mode. i assume it's safe to
                        // override this post-visual setting and just set the editor
                        // to the document's vim mode.
                        setVimKeyMap(preKeyMap);
                    } else {
                        // user was in visual mode before clicking into this editor
                        // so re-enable visual mode if possible.
                        //
                        // known bug: clicking back and forth from inline to full editor
                        //            can sometimes cause the vim bar to state "Normal"
                        //            when in fact the focused inline editor is still in
                        //            visual mode. I don't know how often this bug will
                        //            come up during regular use but it's worth documenting.
                        CodeMirror.keyMap.vim.V(fecm);
                    }
                } else {
                    if (fecm.state.vim) {
                        if (fecm.state.vim.visualMode === true) {
                            // edge case: current editor is in visual mode, so previous editor
                            // must have been in visual mode too, but now no longer
                            // registers as such, so make fecm exit visual mode in return.
                            CodeMirror.keyMap.vim.Esc(fecm);
                        }
                        // set previous mode to current mode.
                        setVimKeyMap(preKeyMap);
                    }
                }

                // detect whether focusedEditor is widget by looking up private
                // _visibleRange. this is the only reliable way that i've discovered
                // to check if inline.
                if (focusedEditor._visibleRange) {
                    // remove then add again so we don't get multiple listeners
                    // for the same event. really piles up after a while.
                    // TODO: these events are not being called
                    fecm.off("keydown", _escKeyEvent);
                    fecm.on("keydown", _escKeyEvent);
                }
            }
        }
    }

    exports.init = init;
    exports.changeDocument = changeDocument;
    exports.attachVimderbar = attachVimderbar;
});
<commit_msg>Vim keys often don't clear away properly (e.g., pressing i, or u)<commit_after>/* jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/* global define, $, brackets, setTimeout */

// this function's purpose is to make CodeMirror's vim keymap play nice with
// Brackets.
define(function (require, exports) {
    "use strict";

    var CodeMirror      = brackets.getModule("thirdparty/CodeMirror2/lib/codemirror"),
        CommandManager  = brackets.getModule("command/CommandManager"),
        DocumentManager = brackets.getModule("document/DocumentManager"),
        EditorManager   = brackets.getModule("editor/EditorManager"),
        Commands        = brackets.getModule("command/Commands"),
        CommandDialog,
        cm,
        fecm,
        precm,
        vimMode,
        controller,
        commandDone;

    /**
     * Initialize Vim fixes for CodeMirror -> Brackets integration.
     * @param {CodeMirror} _cm Current CodeMirror instance.
     * @param {CommandDialog} _CommandDialog Current CommandDialog instance.
     * @param {Object} _controller Vim enable/disable controller functions.
     */
    function init(_cm, _CommandDialog, _controller) {
        CommandDialog = _CommandDialog;
        cm = _cm;
        controller = _controller;

        // CodeMirror -> Brackets Command Hooks
        CodeMirror.commands.save = function () {
            CommandManager.execute("file.save");
        };
        CodeMirror.commands.close = function () {
            var hostEditor = EditorManager.getCurrentFullEditor(),
                inlineFocused = EditorManager.getFocusedInlineWidget();
            if (inlineFocused) {
                // inline editor exists & is in focus. close it.
                EditorManager.closeInlineWidget(hostEditor, inlineFocused);
            } else {
                // no inline editor is in focus so just close the document.
                CommandManager.execute("file.close");
            }
        };
        CodeMirror.commands.open = function () {
            // used to be "file.open" because quickOpen would automatically close
            // following the user's push of Enter key to submit Open command (":e[Enter]")
            // setTimeout meant to give the user a moment to let go of the Enter key.
            setTimeout(function () {
                CommandManager.execute("navigate.quickOpen");
            }, 150);
        };

        // init inline editor fix
        $(EditorManager).on("activeEditorChange", changeEditor);
        // add event listener in case user decides to disable vimderbar.
        $(EditorManager).on("vimderbarDisabled", function () {
            $(EditorManager).off("activeEditorChange", changeEditor);
        });

        // Ex Command Definitions
        CodeMirror.Vim.defineEx("quit", "q", function (cm) {
            cm.focus();
            CodeMirror.commands.close(cm);
        });
        CodeMirror.Vim.defineEx("edit", "e", function (cm) {
            CodeMirror.commands.open(cm);
        });
        CodeMirror.Vim.defineEx("write", "w", function (cm) {
            CodeMirror.commands.save(cm);
        });
        CodeMirror.Vim.defineEx("bnext", "bn", function (cm) {
            var file = DocumentManager.getNextPrevFile(1);
            CommandManager.execute(Commands.FILE_OPEN, { fullPath: file.fullPath });
            cm.focus();
        });
        CodeMirror.Vim.defineEx("bprev", "bp", function (cm) {
            var file = DocumentManager.getNextPrevFile(-1);
            CommandManager.execute(Commands.FILE_OPEN, { fullPath: file.fullPath });
            cm.focus();
        });
        CodeMirror.Vim.defineEx("clearhistory", "clearhistory", function (cm) {
            CommandDialog.resetHistory();
            cm.focus();
        });
    }
    /**
     * @private
     * Handler for CodeMirror's vim.js vim-keypress event. Filters and updates keys, clears commands when next command input.
     * @param {String} key Key pressed.
     */
    function _onKeypress(key) {
        // fix broken '/' search by calling cmd.find
        if (key === '/') { CommandManager.execute("cmd.find"); }
        // prevent esc from being displayed or clearing the dialog keys
        if (key.match(/Esc/)) { return; }
        cm.updateVimCommandKeys(key);
    }
    /**
     * @private
     * Handler for CodeMirror's vim.js vim-command-done event. Sets commandDone so current command is cleared on next keypress.
     */
    function _onCommandDone() {
        cm.clearVimCommandKeys();
    }
    /**
     * Attach events to current CodeMirror instance.
     * @param {CodeMirror} cm Current CodeMirror instance.
     */
    function attachVimderbar(cm) {
        cm.off("vim-keypress", _onKeypress);
        cm.on("vim-keypress", _onKeypress);
        cm.off("vim-command-done", _onCommandDone);
        cm.on("vim-command-done", _onCommandDone);
    }
    /**
     * Change current document, attach events to new editor.
     * @param {CodeMirror} cm CodeMirror instance to attach to.
     */
    function changeDocument(cm) {
        attachVimderbar(cm);
    }
    // TODO: fix insert cursor in inline editors.
    //    (open inline editor in normal mode. press i from within the inline editor.
    //     notice that the cursor remains "fat".)
    //    fixing this bug will likely fix the visual mode bug described below.

    // TODO: solve the inline-editor dilemma
    /**
     * @private
     * Close inline editor when esc is pressed outside of insert/replace mode.
     * @param {CodeMirror} cm Current CodeMirror instance. Unused.
     * @param {jQuery.Event} e KeyEvent event object.
     */
    function _escKeyEvent(cm, e) {
        if (e.keyCode === 27) {
            CodeMirror.e_stop(e);

            vimMode = fecm.getOption("keyMap");

            if (fecm.state.vim) {
                if (fecm.state.vim && fecm.state.vim.visualMode) {
                    CodeMirror.keyMap.vim.Esc(fecm);
                } else if (vimMode === "vim-insert" || vimMode === "vim-replace") {
                    CodeMirror.keyMap["vim-insert"].Esc(fecm);
                } else {
                    CodeMirror.commands.close(fecm);
                }
            }
        }
    }
    /**
     * Set CodeMirror KeyMap based on mode changes.
     * @param {String} map New map name.
     */
    function setVimKeyMap(map) {
        var mode;
        fecm.setOption("keyMap", map);
        switch (map) {
        case "vim-insert":
            mode = "insert";
            break;
        case "vim-replace":
            fecm.toggleOverwrite(true);
            mode = "replace";
            break;
        default:
            mode = "normal";
        }
        CodeMirror.signal(fecm, "vim-mode-change", {
            mode: mode
        });
    }
    /**
     * Change editor, possibly to inline editor.
     * @param {jQuery.Event} event Active editor change event.
     * @param {Editor} focusedEditor Currently focused editor.
     * @param {Editor} previousEditor Editor that last had focus.
     */
    // TODO: Inline editing reverts to non-vim keymap.
    function changeEditor(event, focusedEditor, previousEditor) {
        if (focusedEditor && previousEditor) {
            // make sure focusedEditor and previousEditor exist.
            // this check is to make sure no errors are thrown when
            // a file is opened.
            fecm = focusedEditor._codeMirror;
            precm = previousEditor._codeMirror;

            // get editors so we don't waste resources when user swaps between
            // documents. Only watch if the focused editor is an inline editor
            // or if the focused editor contains inline editors.
            var currentFullEditor = EditorManager.getCurrentFullEditor(),
                inlineEditors = currentFullEditor.getInlineWidgets();
            // ._visibleRange still the only reliable way to detect whether an
            // editor is inline or not. I'm having a hard time thinking of another
            // way to do this.
            if (focusedEditor._visibleRange || inlineEditors.length !== 0) {
                var feKeyMap = fecm.getOption("keyMap"),
                    preKeyMap = precm.getOption("keyMap");

                // if the keymap isn't one of Vim.js' keymap options, turn the vim on
                // and set it to the proper mode.
                if (feKeyMap !== "vim" && feKeyMap !== "vim-insert" && feKeyMap !== "vim-replace") {
                    // enable vim
                    controller.enable(focusedEditor._codeMirror);
                    // force an esc on the editor to get it to act
                    // normally after change.
                    CodeMirror.keyMap.vim.Esc(fecm);
                    // set vim option
                    setVimKeyMap(preKeyMap);
                    attachVimderbar(fecm);
                }

                if (precm.state.vim && precm.state.vim.visualMode) {
                    // previous state was "visual" so exit whatever mode currently in
                    // and set to visual
                    if (feKeyMap === "vim-insert" || feKeyMap === "vim-replace") {
                        // exit whatever mode currently in.
                        CodeMirror.keyMap["vim-insert"].Esc(fecm);
                    } else {
                        CodeMirror.keyMap.vim.Esc(fecm);
                    }

                    if (preKeyMap === "vim-insert" || preKeyMap === "vim-replace") {
                        // from the user's perspective, document's vim is in
                        // "insert" or "replace" but the code claims document vim was at
                        // some point in visual mode. i assume it's safe to
                        // override this post-visual setting and just set the editor
                        // to the document's vim mode.
                        setVimKeyMap(preKeyMap);
                    } else {
                        // user was in visual mode before clicking into this editor
                        // so re-enable visual mode if possible.
                        //
                        // known bug: clicking back and forth from inline to full editor
                        //            can sometimes cause the vim bar to state "Normal"
                        //            when in fact the focused inline editor is still in
                        //            visual mode. I don't know how often this bug will
                        //            come up during regular use but it's worth documenting.
                        CodeMirror.keyMap.vim.V(fecm);
                    }
                } else {
                    if (fecm.state.vim) {
                        if (fecm.state.vim.visualMode === true) {
                            // edge case: current editor is in visual mode, so previous editor
                            // must have been in visual mode too, but now no longer
                            // registers as such, so make fecm exit visual mode in return.
                            CodeMirror.keyMap.vim.Esc(fecm);
                        }
                        // set previous mode to current mode.
                        setVimKeyMap(preKeyMap);
                    }
                }

                // detect whether focusedEditor is widget by looking up private
                // _visibleRange. this is the only reliable way that i've discovered
                // to check if inline.
                if (focusedEditor._visibleRange) {
                    // remove then add again so we don't get multiple listeners
                    // for the same event. really piles up after a while.
                    // TODO: these events are not being called
                    fecm.off("keydown", _escKeyEvent);
                    fecm.on("keydown", _escKeyEvent);
                }
            }
        }
    }

    exports.init = init;
    exports.changeDocument = changeDocument;
    exports.attachVimderbar = attachVimderbar;
});
<|endoftext|>


<commit_before>/**
 * Container for elements in a child frame.
 *
 * There are two ways to specify a title: set the static `title` property or provide a `title`
 * property in the configuration options. The latter will override the former.
 *
 * @abstract
 * @class
 * @extends OO.ui.Element
 * @mixins OO.EventEmitter
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {string|Function} [title] Title string or function that returns a string
 * @cfg {string} [icon] Symbolic name of icon
 * @fires initialize
 */
OO.ui.Window = function OoUiWindow( config ) {
	// Parent constructor
	OO.ui.Window.super.call( this, config );

	// Mixin constructors
	OO.EventEmitter.call( this );

	// Properties
	this.visible = false;
	this.opening = false;
	this.closing = false;
	this.title = OO.ui.resolveMsg( config.title || this.constructor.static.title );
	this.icon = config.icon || this.constructor.static.icon;
	this.frame = new OO.ui.Frame( { '$': this.$ } );
	this.$frame = this.$( '
' );
	this.$ = function () {
		throw new Error( 'this.$() cannot be used until the frame has been initialized.' );
	};

	// Initialization
	this.$element
		.addClass( 'oo-ui-window' )
		// Hide the window using visibility: hidden; while the iframe is still loading
		// Can't use display: none; because that prevents the iframe from loading in Firefox
		.css( 'visibility', 'hidden' )
		.append( this.$frame );
	this.$frame
		.addClass( 'oo-ui-window-frame' )
		.append( this.frame.$element );

	// Events
	this.frame.connect( this, { 'load': 'initialize' } );
};

/* Setup */

OO.inheritClass( OO.ui.Window, OO.ui.Element );
OO.mixinClass( OO.ui.Window, OO.EventEmitter );

/* Events */

/**
 * Initialize contents.
 *
 * Fired asynchronously after construction when iframe is ready.
 *
 * @event initialize
 */

/**
 * Open window.
 *
 * Fired after window has been opened.
 *
 * @event open
 * @param {Object} data Window opening data
 */

/**
 * Close window.
 *
 * Fired after window has been closed.
 *
 * @event close
 * @param {Object} data Window closing data
 */

/* Static Properties */

/**
 * Symbolic name of icon.
 *
 * @static
 * @inheritable
 * @property {string}
 */
OO.ui.Window.static.icon = 'window';

/**
 * Window title.
 *
 * Subclasses must implement this property before instantiating the window.
 * Alternatively, override #getTitle with an alternative implementation.
 *
 * @static
 * @abstract
 * @inheritable
 * @property {string|Function} Title string or function that returns a string
 */
OO.ui.Window.static.title = null;

/* Methods */

/**
 * Check if window is visible.
 *
 * @return {boolean} Window is visible
 */
OO.ui.Window.prototype.isVisible = function () {
	return this.visible;
};

/**
 * Check if window is opening.
 *
 * @return {boolean} Window is opening
 */
OO.ui.Window.prototype.isOpening = function () {
	return this.opening;
};

/**
 * Check if window is closing.
 *
 * @return {boolean} Window is closing
 */
OO.ui.Window.prototype.isClosing = function () {
	return this.closing;
};

/**
 * Get the window frame.
 *
 * @return {OO.ui.Frame} Frame of window
 */
OO.ui.Window.prototype.getFrame = function () {
	return this.frame;
};

/**
 * Get the title of the window.
 *
 * @return {string} Title text
 */
OO.ui.Window.prototype.getTitle = function () {
	return this.title;
};

/**
 * Get the window icon.
 *
 * @return {string} Symbolic name of icon
 */
OO.ui.Window.prototype.getIcon = function () {
	return this.icon;
};

/**
 * Set the size of window frame.
 *
 * @param {number} [width=auto] Custom width
 * @param {number} [height=auto] Custom height
 * @chainable
 */
OO.ui.Window.prototype.setSize = function ( width, height ) {
	if ( !this.frame.$content ) {
		return;
	}

	this.frame.$element.css( {
		'width': width === undefined ? 'auto' : width,
		'height': height === undefined ? 'auto' : height
	} );

	return this;
};

/**
 * Set the title of the window.
 *
 * @param {string|Function} title Title text or a function that returns text
 * @chainable
 */
OO.ui.Window.prototype.setTitle = function ( title ) {
	this.title = OO.ui.resolveMsg( title );
	if ( this.$title ) {
		this.$title.text( title );
	}
	return this;
};

/**
 * Set the icon of the window.
 *
 * @param {string} icon Symbolic name of icon
 * @chainable
 */
OO.ui.Window.prototype.setIcon = function ( icon ) {
	if ( this.$icon ) {
		this.$icon.removeClass( 'oo-ui-icon-' + this.icon );
	}
	this.icon = icon;
	if ( this.$icon ) {
		this.$icon.addClass( 'oo-ui-icon-' + this.icon );
	}

	return this;
};

/**
 * Set the position of window to fit with contents.
 *
 * @param {string} left Left offset
 * @param {string} top Top offset
 * @chainable
 */
OO.ui.Window.prototype.setPosition = function ( left, top ) {
	this.$element.css( { 'left': left, 'top': top } );
	return this;
};

/**
 * Set the height of window to fit with contents.
 *
 * @param {number} [min=0] Min height
 * @param {number} [max] Max height (defaults to content's outer height)
 * @chainable
 */
OO.ui.Window.prototype.fitHeightToContents = function ( min, max ) {
	var height = this.frame.$content.outerHeight();

	this.frame.$element.css(
		'height', Math.max( min || 0, max === undefined ? height : Math.min( max, height ) )
	);

	return this;
};

/**
 * Set the width of window to fit with contents.
 *
 * @param {number} [min=0] Min height
 * @param {number} [max] Max height (defaults to content's outer width)
 * @chainable
 */
OO.ui.Window.prototype.fitWidthToContents = function ( min, max ) {
	var width = this.frame.$content.outerWidth();

	this.frame.$element.css(
		'width', Math.max( min || 0, max === undefined ? width : Math.min( max, width ) )
	);

	return this;
};

/**
 * Initialize window contents.
 *
 * The first time the window is opened, #initialize is called when it's safe to begin populating
 * its contents. See #setup for a way to make changes each time the window opens.
 *
 * Once this method is called, this.$$ can be used to create elements within the frame.
 *
 * @fires initialize
 * @chainable
 */
OO.ui.Window.prototype.initialize = function () {
	// Properties
	this.$ = this.frame.$;
	this.$title = this.$( '

' )
		.text( this.title );
	this.$icon = this.$( '

' )
		.addClass( 'oo-ui-icon-' + this.icon );
	this.$head = this.$( '

' );
	this.$body = this.$( '

' );
	this.$foot = this.$( '

' );
	this.$overlay = this.$( '

' );

	// Initialization
	this.frame.$content.append(
		this.$head.append( this.$icon, this.$title ),
		this.$body,
		this.$foot,
		this.$overlay
	);

	// Undo the visibility: hidden; hack from the constructor and apply display: none;
	// We can do this safely now that the iframe has initialized
	this.$element.hide().css( 'visibility', '' );

	this.emit( 'initialize' );

	return this;
};

/**
 * Setup window for use.
 *
 * Each time the window is opened, once it's ready to be interacted with, this will set it up for
 * use in a particular context, based on the `data` argument.
 *
 * When you override this method, you must call the parent method at the very beginning.
 *
 * @abstract
 * @param {Object} [data] Window opening data
 */
OO.ui.Window.prototype.setup = function () {
	// Override to do something
};

/**
 * Tear down window after use.
 *
 * Each time the window is closed, and it's done being interacted with, this will tear it down and
 * do something with the user's interactions within the window, based on the `data` argument.
 *
 * When you override this method, you must call the parent method at the very end.
 *
 * @abstract
 * @param {Object} [data] Window closing data
 */
OO.ui.Window.prototype.teardown = function () {
	// Override to do something
};

/**
 * Open window.
 *
 * Do not override this method. See #setup for a way to make changes each time the window opens.
 *
 * @param {Object} [data] Window opening data
 * @fires open
 * @chainable
 */
OO.ui.Window.prototype.open = function ( data ) {
	if ( !this.opening && !this.closing && !this.visible ) {
		this.opening = true;
		this.frame.run( OO.ui.bind( function () {
			this.$element.show();
			this.visible = true;
			this.emit( 'opening', data );
			this.setup( data );
			// Focus the content div (which has a tabIndex) to inactivate
			// (but not clear) selections in the parent frame.
			// Must happen after setup runs (otherwise focusing it doesn't work)
			// but before 'open' is emitted (so subclasses can give focus to something else)
			this.frame.$content.focus();
			this.emit( 'open', data );
			this.opening = false;
		}, this ) );
	}

	return this;
};

/**
 * Close window.
 *
 * See #teardown for a way to do something each time the window closes.
 *
 * @param {Object} [data] Window closing data
 * @fires close
 * @chainable
 */
OO.ui.Window.prototype.close = function ( data ) {
	if ( !this.opening && !this.closing && this.visible ) {
		this.frame.$content.find( ':focus' ).blur();
		this.closing = true;
		this.$element.hide();
		this.visible = false;
		this.emit( 'closing', data );
		this.teardown( data );
		this.emit( 'close', data );
		this.closing = false;
	}

	return this;
};
<commit_msg>Add a 'ready' event to the window open sequence<commit_after>/**
 * Container for elements in a child frame.
 *
 * There are two ways to specify a title: set the static `title` property or provide a `title`
 * property in the configuration options. The latter will override the former.
 *
 * @abstract
 * @class
 * @extends OO.ui.Element
 * @mixins OO.EventEmitter
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {string|Function} [title] Title string or function that returns a string
 * @cfg {string} [icon] Symbolic name of icon
 * @fires initialize
 */
OO.ui.Window = function OoUiWindow( config ) {
	// Parent constructor
	OO.ui.Window.super.call( this, config );

	// Mixin constructors
	OO.EventEmitter.call( this );

	// Properties
	this.visible = false;
	this.opening = false;
	this.closing = false;
	this.title = OO.ui.resolveMsg( config.title || this.constructor.static.title );
	this.icon = config.icon || this.constructor.static.icon;
	this.frame = new OO.ui.Frame( { '$': this.$ } );
	this.$frame = this.$( '
' );
	this.$ = function () {
		throw new Error( 'this.$() cannot be used until the frame has been initialized.' );
	};

	// Initialization
	this.$element
		.addClass( 'oo-ui-window' )
		// Hide the window using visibility: hidden; while the iframe is still loading
		// Can't use display: none; because that prevents the iframe from loading in Firefox
		.css( 'visibility', 'hidden' )
		.append( this.$frame );
	this.$frame
		.addClass( 'oo-ui-window-frame' )
		.append( this.frame.$element );

	// Events
	this.frame.connect( this, { 'load': 'initialize' } );
};

/* Setup */

OO.inheritClass( OO.ui.Window, OO.ui.Element );
OO.mixinClass( OO.ui.Window, OO.EventEmitter );

/* Events */

/**
 * Initialize contents.
 *
 * Fired asynchronously after construction when iframe is ready.
 *
 * @event initialize
 */

/**
 * Open window.
 *
 * Fired after window has been opened.
 *
 * @event open
 * @param {Object} data Window opening data
 */

/**
 * Close window.
 *
 * Fired after window has been closed.
 *
 * @event close
 * @param {Object} data Window closing data
 */

/* Static Properties */

/**
 * Symbolic name of icon.
 *
 * @static
 * @inheritable
 * @property {string}
 */
OO.ui.Window.static.icon = 'window';

/**
 * Window title.
 *
 * Subclasses must implement this property before instantiating the window.
 * Alternatively, override #getTitle with an alternative implementation.
 *
 * @static
 * @abstract
 * @inheritable
 * @property {string|Function} Title string or function that returns a string
 */
OO.ui.Window.static.title = null;

/* Methods */

/**
 * Check if window is visible.
 *
 * @return {boolean} Window is visible
 */
OO.ui.Window.prototype.isVisible = function () {
	return this.visible;
};

/**
 * Check if window is opening.
 *
 * @return {boolean} Window is opening
 */
OO.ui.Window.prototype.isOpening = function () {
	return this.opening;
};

/**
 * Check if window is closing.
 *
 * @return {boolean} Window is closing
 */
OO.ui.Window.prototype.isClosing = function () {
	return this.closing;
};

/**
 * Get the window frame.
 *
 * @return {OO.ui.Frame} Frame of window
 */
OO.ui.Window.prototype.getFrame = function () {
	return this.frame;
};

/**
 * Get the title of the window.
 *
 * @return {string} Title text
 */
OO.ui.Window.prototype.getTitle = function () {
	return this.title;
};

/**
 * Get the window icon.
 *
 * @return {string} Symbolic name of icon
 */
OO.ui.Window.prototype.getIcon = function () {
	return this.icon;
};

/**
 * Set the size of window frame.
 *
 * @param {number} [width=auto] Custom width
 * @param {number} [height=auto] Custom height
 * @chainable
 */
OO.ui.Window.prototype.setSize = function ( width, height ) {
	if ( !this.frame.$content ) {
		return;
	}

	this.frame.$element.css( {
		'width': width === undefined ? 'auto' : width,
		'height': height === undefined ? 'auto' : height
	} );

	return this;
};

/**
 * Set the title of the window.
 *
 * @param {string|Function} title Title text or a function that returns text
 * @chainable
 */
OO.ui.Window.prototype.setTitle = function ( title ) {
	this.title = OO.ui.resolveMsg( title );
	if ( this.$title ) {
		this.$title.text( title );
	}
	return this;
};

/**
 * Set the icon of the window.
 *
 * @param {string} icon Symbolic name of icon
 * @chainable
 */
OO.ui.Window.prototype.setIcon = function ( icon ) {
	if ( this.$icon ) {
		this.$icon.removeClass( 'oo-ui-icon-' + this.icon );
	}
	this.icon = icon;
	if ( this.$icon ) {
		this.$icon.addClass( 'oo-ui-icon-' + this.icon );
	}

	return this;
};

/**
 * Set the position of window to fit with contents.
 *
 * @param {string} left Left offset
 * @param {string} top Top offset
 * @chainable
 */
OO.ui.Window.prototype.setPosition = function ( left, top ) {
	this.$element.css( { 'left': left, 'top': top } );
	return this;
};

/**
 * Set the height of window to fit with contents.
 *
 * @param {number} [min=0] Min height
 * @param {number} [max] Max height (defaults to content's outer height)
 * @chainable
 */
OO.ui.Window.prototype.fitHeightToContents = function ( min, max ) {
	var height = this.frame.$content.outerHeight();

	this.frame.$element.css(
		'height', Math.max( min || 0, max === undefined ? height : Math.min( max, height ) )
	);

	return this;
};

/**
 * Set the width of window to fit with contents.
 *
 * @param {number} [min=0] Min height
 * @param {number} [max] Max height (defaults to content's outer width)
 * @chainable
 */
OO.ui.Window.prototype.fitWidthToContents = function ( min, max ) {
	var width = this.frame.$content.outerWidth();

	this.frame.$element.css(
		'width', Math.max( min || 0, max === undefined ? width : Math.min( max, width ) )
	);

	return this;
};

/**
 * Initialize window contents.
 *
 * The first time the window is opened, #initialize is called when it's safe to begin populating
 * its contents. See #setup for a way to make changes each time the window opens.
 *
 * Once this method is called, this.$$ can be used to create elements within the frame.
 *
 * @fires initialize
 * @chainable
 */
OO.ui.Window.prototype.initialize = function () {
	// Properties
	this.$ = this.frame.$;
	this.$title = this.$( '

' )
		.text( this.title );
	this.$icon = this.$( '

' )
		.addClass( 'oo-ui-icon-' + this.icon );
	this.$head = this.$( '

' );
	this.$body = this.$( '

' );
	this.$foot = this.$( '

' );
	this.$overlay = this.$( '

' );

	// Initialization
	this.frame.$content.append(
		this.$head.append( this.$icon, this.$title ),
		this.$body,
		this.$foot,
		this.$overlay
	);

	// Undo the visibility: hidden; hack from the constructor and apply display: none;
	// We can do this safely now that the iframe has initialized
	this.$element.hide().css( 'visibility', '' );

	this.emit( 'initialize' );

	return this;
};

/**
 * Setup window for use.
 *
 * Each time the window is opened, once it's ready to be interacted with, this will set it up for
 * use in a particular context, based on the `data` argument.
 *
 * When you override this method, you must call the parent method at the very beginning.
 *
 * @abstract
 * @param {Object} [data] Window opening data
 */
OO.ui.Window.prototype.setup = function () {
	// Override to do something
};

/**
 * Tear down window after use.
 *
 * Each time the window is closed, and it's done being interacted with, this will tear it down and
 * do something with the user's interactions within the window, based on the `data` argument.
 *
 * When you override this method, you must call the parent method at the very end.
 *
 * @abstract
 * @param {Object} [data] Window closing data
 */
OO.ui.Window.prototype.teardown = function () {
	// Override to do something
};

/**
 * Open window.
 *
 * Do not override this method. See #setup for a way to make changes each time the window opens.
 *
 * @param {Object} [data] Window opening data
 * @fires opening
 * @fires open
 * @fires ready
 * @chainable
 */
OO.ui.Window.prototype.open = function ( data ) {
	if ( !this.opening && !this.closing && !this.visible ) {
		this.opening = true;
		this.frame.run( OO.ui.bind( function () {
			this.$element.show();
			this.visible = true;
			this.emit( 'opening', data );
			this.setup( data );
			this.emit( 'open', data );
			// Focus the content div (which has a tabIndex) to inactivate
			// (but not clear) selections in the parent frame.
			// Must happen after 'open' is emitted (to ensure it is visible)
			// but before 'ready' is emitted (so subclasses can give focus to something else)
			this.frame.$content.focus();
			this.emit( 'ready', data );
			this.opening = false;
		}, this ) );
	}

	return this;
};

/**
 * Close window.
 *
 * See #teardown for a way to do something each time the window closes.
 *
 * @param {Object} [data] Window closing data
 * @fires closing
 * @fires close
 * @chainable
 */
OO.ui.Window.prototype.close = function ( data ) {
	if ( !this.opening && !this.closing && this.visible ) {
		this.frame.$content.find( ':focus' ).blur();
		this.closing = true;
		this.$element.hide();
		this.visible = false;
		this.emit( 'closing', data );
		this.teardown( data );
		this.emit( 'close', data );
		this.closing = false;
	}

	return this;
};
<|endoftext|>


<commit_before><commit_msg>Make sure you check the data after a page is set.<commit_after><|endoftext|>


<commit_before>/**
 * @file Tr component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, Fragment} from 'react';
import PropTypes from 'prop-types';

// Components
import Td from '../_Td';

// Statics
import TableFragment from '../_statics/TableFragment';
import HorizontalAlign from '../_statics/HorizontalAlign';
import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

// Vendors
import classNames from 'classnames';
import Util from '../_vendors/Util';
import TC from '../_vendors/TableCalculation';

class Tr extends Component {

    static Align = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    getDynamicRenderStyle = () => {
        const {useDynamicRender, rowHeight} = this.props;
        return useDynamicRender ? {
                height: rowHeight,
                paddingTop: 0,
                paddingBottom: 0
            }
            :
            null;
    };

    getTdStyle = (column, colIndex, columnsSpan) => {
        const {columnKeyField, columnsWidth, defaultColumnWidth} = this.props;
        return {
            ...column?.bodyStyle,
            ...TC.getStickyColumnStyle(TableFragment.BODY, column?.fixed, colIndex,
                columnsSpan, columnKeyField, columnsWidth, defaultColumnWidth),
            ...this.getDynamicRenderStyle()
        };
    };

    isCollapsed = () => {

        const {idField, data, expandRows} = this.props;

        if (!expandRows || expandRows.length < 1) {
            return true;
        }

        return TC.indexOfNodeInValue(data, expandRows, idField) === -1;

    };

    getColumnsSpan = () => {
        const {columns, data, rowIndex, onRequestColumnsSpan} = this.props;
        return onRequestColumnsSpan ?
            onRequestColumnsSpan(TableFragment.BODY, columns, data, rowIndex)
            :
            TC.getColumnsSpan(TableFragment.BODY, columns, data, rowIndex);
    };

    handleClick = e => {
        const {data, rowIndex, disabled, onRowClick} = this.props;
        !disabled && onRowClick && onRowClick(data, rowIndex, e);
    };

    render() {

        const {

                className, style, columns, rowIndex, data, parentData, tableData,
                isChecked, disabled, ignoreColumnSpan, depth, index, path,
                useDynamicRender, rowHeight,

                ...respProps

            } = this.props,
            collapsed = this.isCollapsed(),
            columnsSpan = this.getColumnsSpan(),
            nodePath = path || [{index, node: data}];

        return (
            <Fragment>

                    {
                        columnsSpan && columnsSpan.map(({column, span}, colIndex) => column ?
                            <Td {...respProps}
                                key={colIndex}
                                className={classNames(column.bodyClassName, {
                                    'fixed-left': column.fixed === HorizontalAlign.LEFT,
                                    'last-fixed-left': column.fixed === HorizontalAlign.LEFT
                                        && columnsSpan?.[colIndex + 1]?.column?.fixed !== HorizontalAlign.LEFT,
                                    'fixed-right': column.fixed === HorizontalAlign.RIGHT,
                                    'first-fixed-right': column.fixed === HorizontalAlign.RIGHT
                                        && columnsSpan?.[colIndex - 1]?.column?.fixed !== HorizontalAlign.RIGHT
                                })}
                                style={this.getTdStyle(column, colIndex, columnsSpan)}
                                rowIndex={rowIndex}
                                colIndex={colIndex}
                                data={data}
                                title={column.bodyTitle}
                                parentData={parentData}
                                tableData={tableData}
                                collapsed={collapsed}
                                renderer={column.bodyRenderer}
                                align={column.bodyAlign || column.align}
                                span={ignoreColumnSpan ? null : span}
                                disabled={disabled}
                                noWrap={TC.handleNoWrap(column.bodyNoWrap, column.noWrap, {
                                    data,
                                    rowIndex,
                                    colIndex: colIndex,
                                    tableData
                                })}
                                depth={depth}
                                path={nodePath}
                                sortable={column.sortable}
                                sortingProp={column.sortingProp}/>
                            :
                            null
                        )
                    }

                {
                    /*rowHasChildren && */!collapsed && data && data.children && data.children.length > 0 ?
                    data.children.map((item, index) =>
                        <Tr {...this.props}
                            key={index}
                            index={index}
                            data={item}
                            parentData={data}
                            depth={depth + 1}
                            path={
                                nodePath ?
                                    [...nodePath, {index, node: item}]
                                    :
                                    [{index, node: item}]
                            }/>
                    )
                    :
                    null
                }

            </Fragment>
        );

    }
}

Tr.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    rowIndex: PropTypes.number,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(PropTypes.shape({

        /**
         * unique keyof column.
         */
        key: PropTypes.string,

        /**
         * fixed position of column ( 'left' / 'right' ).
         */
        fixed: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * width of column.
         */
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * minimum width of column.
         */
        minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * align of current column.
         */
        align: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * no wrap of current column.
         */
        noWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table head.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        headRenderer: PropTypes.any,

        /**
         * The title in table head.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        headTitle: PropTypes.any,

        /**
         * column span of table header.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        headSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table header.
         */
        headNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * align of table body cell.
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table body.
         *  (1) callback:
         *      function (rowData, rowIndex, colIndex, parentData, tableData, collapsed, depth, path) {
         *          return rowData.id;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        bodyRenderer: PropTypes.any,

        /**
         * The title in table body.
         *  (1) callback:
         *      function (rowData, rowIndex, colIndex, parentData, tableData, collapsed, depth, path) {
         *          return rowData.id;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        bodyTitle: PropTypes.any,

        /**
         * column span of table body.
         *  (1) function callback:
         *      function (rowData, colIndex, rowIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        bodySpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table body.
         */
        bodyNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of footer.
         */
        footClassName: PropTypes.string,

        /**
         * Override the styles of footer.
         */
        footStyle: PropTypes.object,

        /**
         * align of table footer cell
         */
        footAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table foot.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        footRenderer: PropTypes.any,

        /**
         * The title in table foot.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        footTitle: PropTypes.any,

        /**
         * column span of table foot.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        footSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table foot.
         */
        footNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * If true,this column can be sorted.
         */
        sortable: PropTypes.bool,

        /**
         * Sorting property.
         */
        sortingProp: PropTypes.string,

        defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType))

    })).isRequired,
    columnKeyField: PropTypes.string,
    columnsWidth: PropTypes.object,
    data: PropTypes.object,
    parentData: PropTypes.object,
    tableData: PropTypes.array,
    isChecked: PropTypes.bool,
    disabled: PropTypes.bool,
    expandRows: PropTypes.array,
    depth: PropTypes.number,
    index: PropTypes.number,
    path: PropTypes.array,
    idField: PropTypes.string,
    isMouseEventForbidden: PropTypes.bool,
    ignoreColumnSpan: PropTypes.bool,
    scrollEl: PropTypes.object,

    /**
     * sorting
     */
    sorting: PropTypes.shape({
        prop: PropTypes.string,
        type: PropTypes.oneOf(Util.enumerateValue(SortingType))
    }),

    /**
     * Dynamic Render
     */
    useDynamicRender: PropTypes.bool,
    rowHeight: PropTypes.number,

    /**
     * column resizable
     */
    defaultColumnWidth: PropTypes.number,

    /**
     * callback
     */
    onRowClick: PropTypes.func,
    onCellClick: PropTypes.func,
    onExpandChange: PropTypes.func,
    onRequestColumnsSpan: PropTypes.func

};

Tr.defaultProps = {

    columnKeyField: 'key',
    rowIndex: 0,
    isChecked: false,
    disabled: false,
    expandRows: [],
    depth: 0,
    idField: 'id',
    isMouseEventForbidden: false,
    ignoreColumnSpan: false,

    /**
     * Dynamic Render
     */
    useDynamicRender: false,
    rowHeight: 50,

    /**
     * column resizable
     */
    defaultColumnWidth: 100

};

export default Tr;
<commit_msg>update Tr<commit_after>/**
 * @file Tr component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, Fragment} from 'react';
import PropTypes from 'prop-types';

// Components
import Td from '../_Td';

// Statics
import TableFragment from '../_statics/TableFragment';
import HorizontalAlign from '../_statics/HorizontalAlign';
import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

// Vendors
import classNames from 'classnames';
import Util from '../_vendors/Util';
import TC from '../_vendors/TableCalculation';

class Tr extends Component {

    static Align = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    getDynamicRenderStyle = () => {
        const {useDynamicRender, rowHeight} = this.props;
        return useDynamicRender ? {
                height: rowHeight,
                paddingTop: 0,
                paddingBottom: 0
            }
            :
            null;
    };

    getTdStyle = (column, colIndex) => {
        const {columns, columnKeyField, columnsWidth, defaultColumnWidth} = this.props;
        return {
            ...column?.bodyStyle,
            ...TC.getStickyColumnStyle(column?.fixed, colIndex, columns,
                columnKeyField, columnsWidth, defaultColumnWidth),
            ...this.getDynamicRenderStyle()
        };
    };

    isCollapsed = () => {

        const {idField, data, expandRows} = this.props;

        if (!expandRows || expandRows.length < 1) {
            return true;
        }

        return TC.indexOfNodeInValue(data, expandRows, idField) === -1;

    };

    getColumnsSpan = () => {
        const {columns, data, rowIndex, onRequestColumnsSpan} = this.props;
        return onRequestColumnsSpan ?
            onRequestColumnsSpan(TableFragment.BODY, columns, data, rowIndex)
            :
            TC.getColumnsSpan(TableFragment.BODY, columns, data, rowIndex);
    };

    handleClick = e => {
        const {data, rowIndex, disabled, onRowClick} = this.props;
        !disabled && onRowClick && onRowClick(data, rowIndex, e);
    };

    render() {

        const {

                className, style, columns, rowIndex, data, parentData, tableData,
                isChecked, disabled, ignoreColumnSpan, depth, index, path,
                useDynamicRender, rowHeight,

                ...respProps

            } = this.props,
            collapsed = this.isCollapsed(),
            columnsSpan = this.getColumnsSpan(),
            nodePath = path || [{index, node: data}];

        return (
            <Fragment>

                    {
                        columnsSpan && columnsSpan.map(({column, span}, colIndex) => column ?
                            <Td {...respProps}
                                key={colIndex}
                                className={classNames(column.bodyClassName, {
                                    'fixed-left': column.fixed === HorizontalAlign.LEFT,
                                    'last-fixed-left': column.fixed === HorizontalAlign.LEFT
                                        && columnsSpan?.[colIndex + 1]?.column?.fixed !== HorizontalAlign.LEFT,
                                    'fixed-right': column.fixed === HorizontalAlign.RIGHT,
                                    'first-fixed-right': column.fixed === HorizontalAlign.RIGHT
                                        && columnsSpan?.[colIndex - 1]?.column?.fixed !== HorizontalAlign.RIGHT
                                })}
                                style={this.getTdStyle(column, colIndex)}
                                rowIndex={rowIndex}
                                colIndex={colIndex}
                                data={data}
                                title={column.bodyTitle}
                                parentData={parentData}
                                tableData={tableData}
                                collapsed={collapsed}
                                renderer={column.bodyRenderer}
                                align={column.bodyAlign || column.align}
                                span={ignoreColumnSpan ? null : span}
                                disabled={disabled}
                                noWrap={TC.handleNoWrap(column.bodyNoWrap, column.noWrap, {
                                    data,
                                    rowIndex,
                                    colIndex: colIndex,
                                    tableData
                                })}
                                depth={depth}
                                path={nodePath}
                                sortable={column.sortable}
                                sortingProp={column.sortingProp}/>
                            :
                            null
                        )
                    }

                {
                    /*rowHasChildren && */!collapsed && data && data.children && data.children.length > 0 ?
                    data.children.map((item, index) =>
                        <Tr {...this.props}
                            key={index}
                            index={index}
                            data={item}
                            parentData={data}
                            depth={depth + 1}
                            path={
                                nodePath ?
                                    [...nodePath, {index, node: item}]
                                    :
                                    [{index, node: item}]
                            }/>
                    )
                    :
                    null
                }

            </Fragment>
        );

    }
}

Tr.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    rowIndex: PropTypes.number,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(PropTypes.shape({

        /**
         * unique keyof column.
         */
        key: PropTypes.string,

        /**
         * fixed position of column ( 'left' / 'right' ).
         */
        fixed: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * width of column.
         */
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * minimum width of column.
         */
        minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * align of current column.
         */
        align: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * no wrap of current column.
         */
        noWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table head.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        headRenderer: PropTypes.any,

        /**
         * The title in table head.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        headTitle: PropTypes.any,

        /**
         * column span of table header.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        headSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table header.
         */
        headNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * align of table body cell.
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table body.
         *  (1) callback:
         *      function (rowData, rowIndex, colIndex, parentData, tableData, collapsed, depth, path) {
         *          return rowData.id;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        bodyRenderer: PropTypes.any,

        /**
         * The title in table body.
         *  (1) callback:
         *      function (rowData, rowIndex, colIndex, parentData, tableData, collapsed, depth, path) {
         *          return rowData.id;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        bodyTitle: PropTypes.any,

        /**
         * column span of table body.
         *  (1) function callback:
         *      function (rowData, colIndex, rowIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        bodySpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table body.
         */
        bodyNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * The class name of footer.
         */
        footClassName: PropTypes.string,

        /**
         * Override the styles of footer.
         */
        footStyle: PropTypes.object,

        /**
         * align of table footer cell
         */
        footAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table foot.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        footRenderer: PropTypes.any,

        /**
         * The title in table foot.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        footTitle: PropTypes.any,

        /**
         * column span of table foot.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        footSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * no wrap of table foot.
         */
        footNoWrap: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

        /**
         * If true,this column can be sorted.
         */
        sortable: PropTypes.bool,

        /**
         * Sorting property.
         */
        sortingProp: PropTypes.string,

        defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType))

    })).isRequired,
    columnKeyField: PropTypes.string,
    columnsWidth: PropTypes.object,
    data: PropTypes.object,
    parentData: PropTypes.object,
    tableData: PropTypes.array,
    isChecked: PropTypes.bool,
    disabled: PropTypes.bool,
    expandRows: PropTypes.array,
    depth: PropTypes.number,
    index: PropTypes.number,
    path: PropTypes.array,
    idField: PropTypes.string,
    isMouseEventForbidden: PropTypes.bool,
    ignoreColumnSpan: PropTypes.bool,
    scrollEl: PropTypes.object,

    /**
     * sorting
     */
    sorting: PropTypes.shape({
        prop: PropTypes.string,
        type: PropTypes.oneOf(Util.enumerateValue(SortingType))
    }),

    /**
     * Dynamic Render
     */
    useDynamicRender: PropTypes.bool,
    rowHeight: PropTypes.number,

    /**
     * column resizable
     */
    defaultColumnWidth: PropTypes.number,

    /**
     * callback
     */
    onRowClick: PropTypes.func,
    onCellClick: PropTypes.func,
    onExpandChange: PropTypes.func,
    onRequestColumnsSpan: PropTypes.func

};

Tr.defaultProps = {

    columnKeyField: 'key',
    rowIndex: 0,
    isChecked: false,
    disabled: false,
    expandRows: [],
    depth: 0,
    idField: 'id',
    isMouseEventForbidden: false,
    ignoreColumnSpan: false,

    /**
     * Dynamic Render
     */
    useDynamicRender: false,
    rowHeight: 50,

    /**
     * column resizable
     */
    defaultColumnWidth: 100

};

export default Tr;
<|endoftext|>


<commit_before>/*jshint node:true, strict:true */
'use strict';

var fs = require('fs');
var util = require('util');

var async = require('async');
var request = require('request');
var cheerio = require('cheerio');
var github = require('octonode');
var xml2js = require('xml2js');

var client = github.client({
  id: process.env.GITHUB_ID,
  secret: process.env.GITHUB_SECRET
});

// HTTP TRAFFIC:
//  FIND USERS:
//    <(REPOS / 10) + 1> GET to github.com
//  FIND REPOS:
//    <UNIQUE_USERS> GET to api.github.com
//  GET BLOCKS:
//    <REPOS * 3> GET to api.github.com
//    <REPOS * 2> GET to github.com
function generate (cb) {
  async.seq(scrape, _uniqueUsersForRepos, _saveUsers, findRepos, _saveRepos, getBlocks, _saveBlocks)(function (err, data) {
    cb(err, data);
  });
}
module.exports.generate = generate;

// NB - scrape until global search is available via API 😁
//  https://developer.github.com/changes/2013-10-18-new-code-search-requirements/
function scrapeSearchResultsPageForRepos(page, callback) {
  var url = util.format('https://github.com/search?p=%s&q=cinder+path%3A%2Fcinderblock.xml&type=Code', page);
  // var url = util.format('https://github.com/search?p=%s&q=cinderblock.xml+in%3Apath&type=Code', page);
  request.get(url, {}, function (err, res, body) {
    if (err) {
      callback(err);
      return;
    }
    if (res.statusCode != 200) {
      callback({statusCode: res.statusCode});
      return;
    }

    var repos = [];
    var $ = cheerio.load(body);
    $('div.code-list-item').each(function () {
      var href = $(this).find('p.title a').attr('href');
      // trim to fullname, USER/REPO
      var fullname = /\/?([\w-]+\/[\w-]+)$/.exec(href)[1];
      repos.push(fullname);
    });

    callback(null, repos);
  });
}

function searchUser(user, callback) {
  client.search().code({
    q: util.format('cinderblock.xml+in:path+user:%s', user)
    // q: util.format('path:cinderblock.xml+user:%s', user)
  }, function (err, data, headers) {
    if (err) {
      // retry after a delay if rate limited.
      // NB - oddly headers is not populated but in err.headers
      if (err.statusCode == 403 && parseInt(err.headers['x-ratelimit-remaining'], 10) == 0) {
        var s = err.headers['x-ratelimit-reset'];
        var ms = new Date(s * 1000) - new Date();
        setTimeout(function () { searchUser(user, callback); }, ms);
        console.log('rate limited till %d, waiting %d ms to retry', s, ms);
        return;
      } else {
        callback(err);
        return;
      }
    }

    var result = data.items.map(function (item) { return item.repository.full_name; });
    callback(null, result);
  });
};

function getBlock(fullName, callback) {
  var repo = client.repo(fullName);
  var defaultBranch;

  async.seq(_info, _commit, _samples, _png, _xml)(callback);

  function _info(cb) {
    repo.info(function (err, data, headers) {
      if (err) {
        cb(err);
        return;
      }

      var block = {
        id: data.id,
        name: data.name,
        full_name: data.full_name,
        user: {
          id: data.owner.id,
          name: data.owner.login,
          url: data.owner.html_url,
          avatar_url: data.owner.avatar_url
        },
        description: data.description,
        url: data.html_url,
        created: data.created_at,
        // NB - date.updated_at reflects repo metadata changes too, not just commits
        updated: data.updated_at,
        commit: {
          date: null
        },
        star_count: data.stargazers_count,
        image_url: null,
        sample_count: 0,
        supports: [],
        template_count: 0,
        forks: []
      };

      defaultBranch = data.default_branch;

      cb(null, block);
    });
  }

  function _commit(block, cb) {
    repo.branch(defaultBranch, function (err, data, headers) {
      if (err) {
        cb(err);
        return;
      }

      // author vs committer
      block.commit.date = data.commit.commit.author.date

      cb(null, block);
    });
  }

  function _samples(block, cb) {
    repo.contents('samples', function (err, data, headers) {
      if (err) {
        // 404 is non-fatal
        if (err.statusCode === 404) {
          cb(null, block);
          return;
        }

        cb(err);
        return;
      }

      // count every directory as a sample
      var count = data.filter(function (f) { return f.type === 'dir'; }).length;
      block.sample_count = count;
      cb(null, block);
    });
  }

  function _png(block, cb) {
    var url = util.format('https://raw.githubusercontent.com/%s/%s/cinderblock.png', block.full_name, defaultBranch);
    hasResource(url, function (err, exists) {
      if (err) {
        cb(err);
        return;
      }

      block.image_url = exists ? url : null;
      cb(null, block);
    });
  }

  function _xml(block, cb) {
    var url = util.format('https://raw.githubusercontent.com/%s/%s/cinderblock.xml', block.full_name, defaultBranch);
    readResource(url, function (err, data) {
      if (err) {
        cb(err);
        return;
      }

      var parser = new xml2js.Parser();
      parser.parseString(data, function (err, result) {
        if (err) {
          console.error('ERROR - failed to parse cinderblock.xml for %s, skipping...', block.full_name);
          // NB - skip this block, don't kill the entire process
          cb(null, null);
          return;
        }

        var rootElement = result.cinder;
        var blockElement = rootElement.block[0];
        block.description = block.description || blockElement.$.summary || '';
        block.supports = blockElement.supports ? blockElement.supports.map(function (e) {
          return e.$.os || null;
        }).filter(function (e) { return e !== null; }) : [];
        block.template_count = rootElement.template ? rootElement.template.length : 0;

        cb(null, block);
      });
    });
  }
}

function hasResource(url, callback) {
  request.head(url, {}, function (err, res, body) {
    if (err) {
      callback(err);
      return;
    }

    callback(null, res.statusCode === 200);
  });
}

function readResource(url, callback) {
  request.get(url, {}, function (err, res, body) {
    if (err) {
      callback(err);
      return;
    }

    callback(null, body);
  });
}

// wrappers
function scrape(cb) {
  var repos = [];
  var status = true;
  var page = 1;

  async.whilst(function () {
    return status;
  }, function (callback) {
    scrapeSearchResultsPageForRepos(page++, function (err, data) {
      if (err) {
        callback(err);
        return;
      }

      repos = repos.concat(data);
      status = data.length > 0;
      callback();
    });
  }, function (err) {
    // reject any repo that occurs more than once, not likely a block
    var results = repos.filter(function (elem, idx, array) {
      return array.indexOf(elem) === array.lastIndexOf(elem);
    });
    console.log('found %d repos, via scraping', results.length);

    cb(err, results);
  });
}

function _uniqueUsersForRepos(data, cb) {
  var results = data.map(function (fullname) {
    // USER/REPO to USER
    return /([\w-]+)\/[\w-]+$/.exec(fullname)[1];
  }).filter(function (elem, idx, array) {
    // unique users
    return array.indexOf(elem) == idx;
  });
  console.log('%d unique users', results.length);

  cb(null, results);
}

function _saveUsers(data, cb) {
  fs.writeFile('./_users.json', JSON.stringify(data), function (err) {
    cb(err, data);
  });
}

function _readUsers(cb) {
  fs.readFile('./_users.json', function (err, data) {
    if (err) {
      cb(err);
      return;
    }

    var repos = JSON.parse(data);
    cb(null, repos);
  });
}

function findRepos(data, cb) {
  async.concatSeries(data, searchUser, function (err, results) {
    if (err) {
      cb(err);
      return;
    }

    // reject any repo that occurs more than once, not likely a block
    var repos = results.filter(function (elem, idx, array) {
      return array.indexOf(elem) === array.lastIndexOf(elem);
    });
    console.log('found %d repos', repos.length);

    cb(null, repos);
  });
}

function _saveRepos(data, cb) {
  fs.writeFile('./_repos.json', JSON.stringify(data), function (err) {
    cb(err, data);
  });
}

function _readRepos(cb) {
  fs.readFile('./_repos.json', function (err, data) {
    if (err) {
      cb(err);
      return;
    }

    var repos = JSON.parse(data);
    cb(null, repos);
  });
}

function getBlocks(repos, cb) {
  async.mapLimit(repos, 4, getBlock, function (err, results) {
    // remove nulls representing skipped blocks
    results = results.filter(function (e) { return e !== null; });
    cb(err, results);
  });
}

function _saveBlocks(data, cb) {
  fs.writeFile('./_blocks.json', JSON.stringify(data), function (err) {
    cb(err, data);
  });
}

function _readBlocks(cb) {
  fs.readFile('./_blocks.json', function (err, data) {
    if (err) {
      cb(err);
      return;
    }

    var blocks = JSON.parse(data);
    cb(null, blocks);
  });
}
<commit_msg>add a user whitelist<commit_after>/*jshint node:true, strict:true */
'use strict';

var fs = require('fs');
var util = require('util');

var async = require('async');
var request = require('request');
var cheerio = require('cheerio');
var github = require('octonode');
var xml2js = require('xml2js');

var client = github.client({
  id: process.env.GITHUB_ID,
  secret: process.env.GITHUB_SECRET
});

// HTTP TRAFFIC:
//  FIND USERS:
//    <(REPOS / 10) + 1> GET to github.com
//  FIND REPOS:
//    <UNIQUE_USERS> GET to api.github.com
//  GET BLOCKS:
//    <REPOS * 3> GET to api.github.com
//    <REPOS * 2> GET to github.com
function generate (cb) {
  async.seq(scrape, _uniqueUsersForRepos, _saveUsers, _addMissingUsers, findRepos, _saveRepos, getBlocks, _saveBlocks)(function (err, data) {
    cb(err, data);
  });
}
module.exports.generate = generate;

// NB - scrape until global search is available via API 😁
//  https://developer.github.com/changes/2013-10-18-new-code-search-requirements/
function scrapeSearchResultsPageForRepos(page, callback) {
  var url = util.format('https://github.com/search?p=%s&q=cinder+path%3A%2Fcinderblock.xml&type=Code', page);
  // var url = util.format('https://github.com/search?p=%s&q=cinderblock.xml+in%3Apath&type=Code', page);
  request.get(url, {}, function (err, res, body) {
    if (err) {
      callback(err);
      return;
    }
    if (res.statusCode != 200) {
      callback({statusCode: res.statusCode});
      return;
    }

    var repos = [];
    var $ = cheerio.load(body);
    $('div.code-list-item').each(function () {
      var href = $(this).find('p.title a').attr('href');
      // trim to fullname, USER/REPO
      var fullname = /\/?([\w-]+\/[\w-]+)$/.exec(href)[1];
      repos.push(fullname);
    });

    callback(null, repos);
  });
}

function searchUser(user, callback) {
  client.search().code({
    q: util.format('cinderblock.xml+in:path+user:%s', user)
    // q: util.format('path:cinderblock.xml+user:%s', user)
  }, function (err, data, headers) {
    if (err) {
      // retry after a delay if rate limited.
      // NB - oddly headers is not populated but in err.headers
      if (err.statusCode == 403 && parseInt(err.headers['x-ratelimit-remaining'], 10) == 0) {
        var s = err.headers['x-ratelimit-reset'];
        var ms = new Date(s * 1000) - new Date();
        setTimeout(function () { searchUser(user, callback); }, ms);
        console.log('rate limited till %d, waiting %d ms to retry', s, ms);
        return;
      } else {
        callback(err);
        return;
      }
    }

    var result = data.items.map(function (item) { return item.repository.full_name; });
    callback(null, result);
  });
};

function getBlock(fullName, callback) {
  var repo = client.repo(fullName);
  var defaultBranch;

  async.seq(_info, _commit, _samples, _png, _xml)(callback);

  function _info(cb) {
    repo.info(function (err, data, headers) {
      if (err) {
        cb(err);
        return;
      }

      var block = {
        id: data.id,
        name: data.name,
        full_name: data.full_name,
        user: {
          id: data.owner.id,
          name: data.owner.login,
          url: data.owner.html_url,
          avatar_url: data.owner.avatar_url
        },
        description: data.description,
        url: data.html_url,
        created: data.created_at,
        // NB - date.updated_at reflects repo metadata changes too, not just commits
        updated: data.updated_at,
        commit: {
          date: null
        },
        star_count: data.stargazers_count,
        image_url: null,
        sample_count: 0,
        supports: [],
        template_count: 0,
        forks: []
      };

      defaultBranch = data.default_branch;

      cb(null, block);
    });
  }

  function _commit(block, cb) {
    repo.branch(defaultBranch, function (err, data, headers) {
      if (err) {
        cb(err);
        return;
      }

      // author vs committer
      block.commit.date = data.commit.commit.author.date

      cb(null, block);
    });
  }

  function _samples(block, cb) {
    repo.contents('samples', function (err, data, headers) {
      if (err) {
        // 404 is non-fatal
        if (err.statusCode === 404) {
          cb(null, block);
          return;
        }

        cb(err);
        return;
      }

      // count every directory as a sample
      var count = data.filter(function (f) { return f.type === 'dir'; }).length;
      block.sample_count = count;
      cb(null, block);
    });
  }

  function _png(block, cb) {
    var url = util.format('https://raw.githubusercontent.com/%s/%s/cinderblock.png', block.full_name, defaultBranch);
    hasResource(url, function (err, exists) {
      if (err) {
        cb(err);
        return;
      }

      block.image_url = exists ? url : null;
      cb(null, block);
    });
  }

  function _xml(block, cb) {
    var url = util.format('https://raw.githubusercontent.com/%s/%s/cinderblock.xml', block.full_name, defaultBranch);
    readResource(url, function (err, data) {
      if (err) {
        cb(err);
        return;
      }

      var parser = new xml2js.Parser();
      parser.parseString(data, function (err, result) {
        if (err) {
          console.error('ERROR - failed to parse cinderblock.xml for %s, skipping...', block.full_name);
          // NB - skip this block, don't kill the entire process
          cb(null, null);
          return;
        }

        var rootElement = result.cinder;
        var blockElement = rootElement.block[0];
        block.description = block.description || blockElement.$.summary || '';
        block.supports = blockElement.supports ? blockElement.supports.map(function (e) {
          return e.$.os || null;
        }).filter(function (e) { return e !== null; }) : [];
        block.template_count = rootElement.template ? rootElement.template.length : 0;

        cb(null, block);
      });
    });
  }
}

function hasResource(url, callback) {
  request.head(url, {}, function (err, res, body) {
    if (err) {
      callback(err);
      return;
    }

    callback(null, res.statusCode === 200);
  });
}

function readResource(url, callback) {
  request.get(url, {}, function (err, res, body) {
    if (err) {
      callback(err);
      return;
    }

    callback(null, body);
  });
}

// wrappers
function scrape(cb) {
  var repos = [];
  var status = true;
  var page = 1;

  async.whilst(function () {
    return status;
  }, function (callback) {
    scrapeSearchResultsPageForRepos(page++, function (err, data) {
      if (err) {
        callback(err);
        return;
      }

      repos = repos.concat(data);
      status = data.length > 0;
      callback();
    });
  }, function (err) {
    // reject any repo that occurs more than once, not likely a block
    var results = repos.filter(function (elem, idx, array) {
      return array.indexOf(elem) === array.lastIndexOf(elem);
    });
    console.log('found %d repos, via scraping', results.length);

    cb(err, results);
  });
}

function _uniqueUsersForRepos(data, cb) {
  var results = data.map(function (fullname) {
    // USER/REPO to USER
    return /([\w-]+)\/[\w-]+$/.exec(fullname)[1];
  }).filter(function (elem, idx, array) {
    // unique users
    return array.indexOf(elem) == idx;
  });
  console.log('%d unique users', results.length);

  cb(null, results);
}

function _saveUsers(data, cb) {
  fs.writeFile('./_users.json', JSON.stringify(data), function (err) {
    cb(err, data);
  });
}

function _readUsers(cb) {
  fs.readFile('./_users.json', function (err, data) {
    if (err) {
      cb(err);
      return;
    }

    var repos = JSON.parse(data);
    cb(null, repos);
  });
}

function _addMissingUsers(data, cb) {
  if (!fs.existsSync('./_users-missing.json')) {
    cb(null, data);
    return;
  }

  fs.readFile('./_users-missing.json', function (err, d) {
    if (err) {
      cb(err);
      return;
    }

    var results = data.concat(JSON.parse(d)).filter(function (elem, idx, array) {
      return array.indexOf(elem) === array.lastIndexOf(elem);
    });
    console.log('adding %d missing users', results.length - data.length);

    cb(null, results);
  });
}

function findRepos(data, cb) {
  async.concatSeries(data, searchUser, function (err, results) {
    if (err) {
      cb(err);
      return;
    }

    // reject any repo that occurs more than once, not likely a block
    var repos = results.filter(function (elem, idx, array) {
      return array.indexOf(elem) === array.lastIndexOf(elem);
    });
    console.log('found %d repos', repos.length);

    cb(null, repos);
  });
}

function _saveRepos(data, cb) {
  fs.writeFile('./_repos.json', JSON.stringify(data), function (err) {
    cb(err, data);
  });
}

function _readRepos(cb) {
  fs.readFile('./_repos.json', function (err, data) {
    if (err) {
      cb(err);
      return;
    }

    var repos = JSON.parse(data);
    cb(null, repos);
  });
}

function getBlocks(repos, cb) {
  async.mapLimit(repos, 4, getBlock, function (err, results) {
    // remove nulls representing skipped blocks
    results = results.filter(function (e) { return e !== null; });
    cb(err, results);
  });
}

function _saveBlocks(data, cb) {
  fs.writeFile('./_blocks.json', JSON.stringify(data), function (err) {
    cb(err, data);
  });
}

function _readBlocks(cb) {
  fs.readFile('./_blocks.json', function (err, data) {
    if (err) {
      cb(err);
      return;
    }

    var blocks = JSON.parse(data);
    cb(null, blocks);
  });
}
<|endoftext|>