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

define([
  "app",
  // Libs
  "api",
  "ace/ace"
],

function(app, FauxtonAPI, ace) {
  var Components = app.module();

  Components.Pagination = FauxtonAPI.View.extend({
    template: "templates/fauxton/pagination",

    initialize: function(options) {
      this.page = parseInt(options.page, 10);
      this.perPage = options.perPage;
      this.total = options.total;
      this.totalPages = Math.ceil(this.total / this.perPage);
      this.urlFun = options.urlFun;
    },

    serialize: function() {
      return {
        page: this.page,
        perPage: this.perPage,
        total: this.total,
        totalPages: this.totalPages,
        urlFun: this.urlFun
      };
    }
  });

  Components.IndexPagination = FauxtonAPI.View.extend({
    template: "templates/fauxton/index_pagination",
    events: {
      "click a": 'scrollTo',
      "click a#next": 'nextClicked',
      "click a#previous": 'previousClicked'
    },

    previousIds: [],

    scrollTo: function () {
      if (!this.scrollToSelector) { return; }
      $(this.scrollToSelector).animate({ scrollTop: 0 }, 'slow');
    },

    initialize: function (options) {
      this.previousUrlfn = options.previousUrlfn;
      this.nextUrlfn = options.nextUrlfn;
      this.canShowPreviousfn = options.canShowPreviousfn;
      this.canShowNextfn = options.canShowNextfn;
      this.scrollToSelector = options.scrollToSelector;
      _.bindAll(this);
    },

    previousClicked: function (event) {
      event.preventDefault();
      if (!this.canShowPreviousfn()) { return; }
      FauxtonAPI.navigate(this.previousUrlfn(), {trigger: false});
      FauxtonAPI.triggerRouteEvent('paginate', 'previous');
    },

    nextClicked: function (event) {
      event.preventDefault();
      if (!this.canShowNextfn()) { return; }
      var doc = this.collection.first();

      if (doc) {
        this.previousIds.push(doc.id);
      }

      FauxtonAPI.navigate(this.nextUrlfn(), {trigger: false});
      FauxtonAPI.triggerRouteEvent('paginate', 'next');
    },

    serialize: function () {
      return {
        canShowNextfn: this.canShowNextfn,
        canShowPreviousfn: this.canShowPreviousfn,
      };
    }

  });

  //TODO allow more of the typeahead options.
  //Current this just does what we need but we
  //need to support the other typeahead options.
  Components.Typeahead = FauxtonAPI.View.extend({

    initialize: function (options) {
      this.source = options.source;
      _.bindAll(this);
    },

    afterRender: function () {
      this.$el.typeahead({
        source: this.source
      });
    }

  });

  Components.DbSearchTypeahead = Components.Typeahead.extend({
    initialize: function (options) {
      this.dbLimit = options.dbLimit || 30;
      _.bindAll(this);
    },
    source: function(query, process) {
      var url = [
        app.host,
        "/_all_dbs?startkey=%22",
        query,
        "%22&endkey=%22",
        query,
        "\u9999",
        "%22&limit=",
        this.dbLimit
      ].join('');

      if (this.ajaxReq) { this.ajaxReq.abort(); }

      this.ajaxReq = $.ajax({
        cache: false,
        url: url,
        dataType: 'json',
        success: function(data) {
          process(data);
        }
      });
    }
  });

  Components.DocSearchTypeahead = Components.Typeahead.extend({
    initialize: function (options) {
      this.docLimit = options.docLimit || 30;
      this.database = options.database;
      _.bindAll(this);
    },
    source: function(query, process) {
      var url = [
        app.host,
        "/",
        this.database.id,
        "/_all_docs?startkey=%22",
        query,
        "%22&endkey=%22",
        query,
        "\u9999",
        "%22&limit=",
        this.docLimit
      ].join('');

      if (this.ajaxReq) { this.ajaxReq.abort(); }

      this.ajaxReq = $.ajax({
        cache: false,
        url: url,
        dataType: 'json',
        success: function(data) {
          var ids = _.map(data.rows, function (row) {
            return row.id;
          });
          process(ids);
        }
      });
    }
  });

  Components.Editor = FauxtonAPI.View.extend({
    initialize: function (options) {
      this.editorId = options.editorId;
      this.mode = options.mode || "json";
      this.commands = options.commands;
      this.theme = options.theme || 'crimson_editor';
      this.couchJSHINT = options.couchJSHINT;
    },

    afterRender: function () {
      this.editor = ace.edit(this.editorId);
      this.editor.setTheme("ace/theme/" + this.theme);
      this.editor.getSession().setMode("ace/mode/" + this.mode);
      this.editor.setShowPrintMargin(false);
      this.editor.gotoLine(2);
      this.addCommands();

      if (this.couchJSHINT) {
        this.removeIncorrectAnnotations();
      }
    },

    addCommands: function () {
      _.each(this.commands, function (command) {
        this.editor.commands.addCommand(command);
      }, this);
    },

    removeIncorrectAnnotations: function () {
      var editor = this.editor;

      this.editor.getSession().on("changeAnnotation", function(){
        var annotations = editor.getSession().getAnnotations();

        var newAnnotations = _.reduce(annotations, function (annotations, error) {
          if (!FauxtonAPI.isIgnorableError(error.raw)) {
            annotations.push(error);
          }
          return annotations;
        }, []);

        if (annotations.length !== newAnnotations.length) {
          editor.getSession().setAnnotations(newAnnotations);
        }
      });
    },

    setValue: function (data, lineNumber) {
      lineNumber = lineNumber ? lineNumber : -1;
      this.editor.setValue(data, lineNumber);
    },

    getValue: function () {
      return this.editor.getValue();
    },

    getAnnotations: function () {
      return this.editor.getSession().getAnnotations();
    },

    hadValidCode: function () {
     var errors = this.getAnnotations();
     // By default CouchDB view functions don't pass lint
     return _.every(errors, function(error) {
      return FauxtonAPI.isIgnorableError(error.raw);
      },this);
    }

  });

  return Components;
});

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

define([
  "app",
  // Libs
  "api",
  "ace/ace"
],

function(app, FauxtonAPI, ace) {
  var Components = app.module();

  Components.Pagination = FauxtonAPI.View.extend({
    template: "templates/fauxton/pagination",

    initialize: function(options) {
      this.page = parseInt(options.page, 10);
      this.perPage = options.perPage;
      this.total = options.total;
      this.totalPages = Math.ceil(this.total / this.perPage);
      this.urlFun = options.urlFun;
    },

    serialize: function() {
      return {
        page: this.page,
        perPage: this.perPage,
        total: this.total,
        totalPages: this.totalPages,
        urlFun: this.urlFun
      };
    }
  });

  Components.IndexPagination = FauxtonAPI.View.extend({
    template: "templates/fauxton/index_pagination",
    events: {
      "click a": 'scrollTo',
      "click a#next": 'nextClicked',
      "click a#previous": 'previousClicked'
    },

    previousIds: [],

    scrollTo: function () {
      if (!this.scrollToSelector) { return; }
      $(this.scrollToSelector).animate({ scrollTop: 0 }, 'slow');
    },

    initialize: function (options) {
      this.previousUrlfn = options.previousUrlfn;
      this.nextUrlfn = options.nextUrlfn;
      this.canShowPreviousfn = options.canShowPreviousfn;
      this.canShowNextfn = options.canShowNextfn;
      this.scrollToSelector = options.scrollToSelector;
      _.bindAll(this);
    },

    previousClicked: function (event) {
      event.preventDefault();
      if (!this.canShowPreviousfn()) { return; }
      FauxtonAPI.navigate(this.previousUrlfn(), {trigger: false});
      FauxtonAPI.triggerRouteEvent('paginate', 'previous');
    },

    nextClicked: function (event) {
      event.preventDefault();
      if (!this.canShowNextfn()) { return; }
      var doc = this.collection.first();

      if (doc) {
        this.previousIds.push(doc.id);
      }

      FauxtonAPI.navigate(this.nextUrlfn(), {trigger: false});
      FauxtonAPI.triggerRouteEvent('paginate', 'next');
    },

    serialize: function () {
      return {
        canShowNextfn: this.canShowNextfn,
        canShowPreviousfn: this.canShowPreviousfn,
      };
    }

  });

  //TODO allow more of the typeahead options.
  //Current this just does what we need but we
  //need to support the other typeahead options.
  Components.Typeahead = FauxtonAPI.View.extend({

    initialize: function (options) {
      this.source = options.source;
      _.bindAll(this);
    },

    afterRender: function () {
      this.$el.typeahead({
        source: this.source
      });
    }

  });

  Components.DbSearchTypeahead = Components.Typeahead.extend({
    initialize: function (options) {
      this.dbLimit = options.dbLimit || 30;
      _.bindAll(this);
    },
    source: function(query, process) {
      var url = [
        app.host,
        "/_all_dbs?startkey=%22",
        query,
        "%22&endkey=%22",
        query,
        "\u9999",
        "%22&limit=",
        this.dbLimit
      ].join('');

      if (this.ajaxReq) { this.ajaxReq.abort(); }

      this.ajaxReq = $.ajax({
        cache: false,
        url: url,
        dataType: 'json',
        success: function(data) {
          process(data);
        }
      });
    }
  });

  Components.DocSearchTypeahead = Components.Typeahead.extend({
    initialize: function (options) {
      this.docLimit = options.docLimit || 30;
      this.database = options.database;
      _.bindAll(this);
    },
    source: function(query, process) {
      var url = [
        app.host,
        "/",
        this.database.id,
        "/_all_docs?startkey=%22",
        query,
        "%22&endkey=%22",
        query,
        "\u9999",
        "%22&limit=",
        this.docLimit
      ].join('');

      if (this.ajaxReq) { this.ajaxReq.abort(); }

      this.ajaxReq = $.ajax({
        cache: false,
        url: url,
        dataType: 'json',
        success: function(data) {
          var ids = _.map(data.rows, function (row) {
            return row.id;
          });
          process(ids);
        }
      });
    }
  });

  Components.Editor = FauxtonAPI.View.extend({
    initialize: function (options) {
      this.editorId = options.editorId;
      this.mode = options.mode || "json";
      this.commands = options.commands;
      this.theme = options.theme || 'crimson_editor';
      this.couchJSHINT = options.couchJSHINT;
    },

    afterRender: function () {
      this.editor = ace.edit(this.editorId);
      this.editor.setTheme("ace/theme/" + this.theme);
      this.editor.getSession().setMode("ace/mode/" + this.mode);
      this.editor.getSession().setUseWrapMode(true);
      this.editor.setShowPrintMargin(false);
      this.editor.gotoLine(2);
      this.addCommands();

      if (this.couchJSHINT) {
        this.removeIncorrectAnnotations();
      }
    },

    addCommands: function () {
      _.each(this.commands, function (command) {
        this.editor.commands.addCommand(command);
      }, this);
    },

    removeIncorrectAnnotations: function () {
      var editor = this.editor;

      this.editor.getSession().on("changeAnnotation", function(){
        var annotations = editor.getSession().getAnnotations();

        var newAnnotations = _.reduce(annotations, function (annotations, error) {
          if (!FauxtonAPI.isIgnorableError(error.raw)) {
            annotations.push(error);
          }
          return annotations;
        }, []);

        if (annotations.length !== newAnnotations.length) {
          editor.getSession().setAnnotations(newAnnotations);
        }
      });
    },

    setValue: function (data, lineNumber) {
      lineNumber = lineNumber ? lineNumber : -1;
      this.editor.setValue(data, lineNumber);
    },

    getValue: function () {
      return this.editor.getValue();
    },

    getAnnotations: function () {
      return this.editor.getSession().getAnnotations();
    },

    hadValidCode: function () {
     var errors = this.getAnnotations();
     // By default CouchDB view functions don't pass lint
     return _.every(errors, function(error) {
      return FauxtonAPI.isIgnorableError(error.raw);
      },this);
    }

  });

  return Components;
});

<|endoftext|>


<commit_before>'use strict';

function Block(data) {
    this.kind = m.prop(data.kind);
    this.subkind = m.prop(data.subkind || null);
    this.value = m.prop(data.value);
    /// Array of string or Block. String = a hole of type (string
    /// value), i.e. "method" means we want a method block here.
    this.children = m.prop(data.children || []);
}

Block.prototype.isChildValid = function(index, kind) {
    var child = this.children()[index];
    if (child instanceof Block) {
        return child.kind() === kind;
    }
    return child === kind;
};

Block.prototype.setChild = function(index, block) {
    this.children()[index] = block;
};

Block.prototype.removeChild = function(index) {
    // Deleting sub-block, put a hole back in
    var block = this.children()[index];
    this.children()[index] = block.kind();
    return block;
};

Block.prototype.toString = function() {
    return "Block(kind=" + this.kind() +
        ", subkind=" + this.subkind() +
        ", value=" + this.value() +
        ", children=[" + this.children().join(", ") + "])";
};

Block.newControlFlow = function(subkind, children) {
    children = children || [];
    switch (subkind) {
    case "tell":
        children[0] = children[0] || "object";
        children[1] = children[1] || "method";
        break;
    case "if/else":
        children[0] = children[0] || "boolean";
        break;
    case "forever":
        break;
    default:
        throw "Error: invalid control flow subkind: " + subkind;
    }
    return new Block({
        kind: "control-flow-structure",
        subkind: subkind,
        value: null,
        children: children,
    });
};
// TODO: specify class as well
Block.newMethod = function(name) {
    return new Block({
        kind: "method",
        subkind: null,
        value: name,
    });
};

var GameWidget = {
    controller: function(args) {
        var controller = {
            mapMode: m.prop("minimap"),
        };

        return controller;
    },

    view: function(controller) {
        return m(".container", [
            m.component(MapComponent, {
                mode: controller.mapMode(),
            }),
            m.component(EditorComponent),
        ]);
    },
};

var EditorComponent = {
    controller: function(args) {
        var controller = {
            blocks: m.prop([
                Block.newControlFlow("tell", [
                    "object",
                    Block.newMethod("moveForward"),
                ]),
                Block.newControlFlow("if/else"),
            ]),
            showTrash: m.prop(false),
            handleDrop: handleDrop,
            drake: null,
        };

        function makeBlock(el) {
            if (el.classList.contains("control-flow-structure")) {
                try {
                    return Block.newControlFlow(el.dataset.subkind);
                }
                catch (e) {
                    return null;
                }
            }
            else if (el.classList.contains("primitive")) {
                return new Block({
                    kind: "primitive",
                    subkind: el.innerText,
                    value: null,
                });
            }
            else if (el.classList.contains("method")) {
                // TODO: set class based on source
                return Block.newMethod(el.textContent);
            }
            else if (el.classList.contains("boolean")) {
                return new Block({
                    kind: "boolean",
                    value: el.innerText == "true" ? true : false,
                });
            }
            return null;
        }

        function findBlock(target) {
            var indices = [parseInt(target.dataset.index, 10)];
            target = target.parentNode;
            while (target && target.id !== "block-editor" &&
                   target.id !== "workspace") {
                if (typeof target.dataset.index === "undefined") {
                    target = target.parentNode;
                    continue;
                }

                console.log(target, target.dataset.index);
                indices.push(parseInt(target.dataset.index, 10));
                target = target.parentNode;
            }

            indices.reverse();

            var blockObj = null;
            if (indices.length > 1) {
                blockObj = controller.blocks()[indices[0]];
            }
            for (var i = 1; i < indices.length - 1; i++) {
                blockObj = blockObj.children()[i];
            }

            return {
                block: blockObj,
                indices: indices,
            };
        }

        function handleDrop(el, target, source, sibling) {
            console.log("handle drop", source, target, el.contains(target));
            m.startComputation();
            if (source === target) {
                // TODO: reorder the list
                console.log("reorder", sibling, el, el.previousSibling);
                var result;
                if (source.id === "block-editor") {
                    result = findBlock(el);
                }
                else {

                }

                if (sibling === null) {
                    var lastIndex = result.indices[result.indices.length - 1];
                    var block = controller.blocks().splice(lastIndex, 1)[0];
                    controller.blocks().push(block);
                }

                controller.drake.cancel(true);
            }
            else if (el.contains(target)) {
                controller.drake.cancel(true);
            }
            else if (target === null) {
                console.log("null target");
            }
            else if (target.classList.contains("block-trash")) {
                console.log("delete");
                // target is the deletion zone, so the last index is
                // the index of the block, and blockObj is the parent
                // (or null)

                // Don't bother trying to delete the block if it
                // didn't come from the block editor
                if (!document.getElementById("workbench").contains(source)) {
                    var result;
                    if (source.id === "block-editor") {
                        result = findBlock(el);
                    }
                    else {
                        result = findBlock(source);
                    }
                    var lastIndex = result.indices[result.indices.length - 1];
                    console.log(result);
                    if (result.block) {
                        result.block.removeChild(lastIndex);
                    }
                    else {
                        controller.blocks().splice(lastIndex, 1);
                    }
                }
                controller.drake.cancel(true);
            }
            else if (source.classList.contains("block-hole") &&
                     target.classList.contains("block-hole")) {
                var result = findBlock(source);
                var newParent = findBlock(target);
                var lastIndex = result.indices[result.indices.length - 1];
                var newIndex = newParent.indices[newParent.indices.length - 1];
                if (result.block && newParent.block) {
                    var block = result.block.removeChild(lastIndex);
                    if (newParent.block.isChildValid(newIndex, block.kind())) {
                        newParent.block.setChild(newIndex, block);
                    }
                    else {
                        result.block.setChild(lastIndex, block);
                        // Force dragula to revert the drop. We can't
                        // use m.redraw.strategy("all") since this
                        // recreates the controller, resetting the UI.
                        controller.drake.cancel(true);
                    }
                }
                else {
                    console.log("ERROR: could not find block hole");
                }

                // TODO: swap if both holes are filled?
                controller.drake.remove();
            }
            else if (source.parentNode.classList.contains("workbench-area")) {
                console.log("new block");
                var block = makeBlock(el);

                if (block) {
                    if (target.id === "block-editor") {
                        if (block.kind() === "control-flow-structure") {
                            controller.blocks().push(block);
                        }
                        else {
                            // TODO: some sort of error message +
                            // visual indicator
                        }
                    }
                    else {
                        // target is a hole, so the last index is the
                        // index of the hole, and blockObj is the
                        // parent
                        var result = findBlock(target);
                        var indices = result.indices;
                        var blockObj = result.block;
                        var childIndex = result.indices[result.indices.length - 1];

                        // Make sure child is of correct type
                        var children = blockObj.children();
                        var blockType = children[childIndex] instanceof Block ?
                            children[childIndex].kind() :
                            children[childIndex];
                        if (block.kind() === blockType) {
                            blockObj.children()[childIndex] = block;
                        }
                        else {
                            // TODO: error + visual indicator
                        }
                    }

                    // TODO: if source was another block, put a hole
                    // in that block
                }

                controller.drake.remove();
            }

            console.log(controller.blocks());

            m.endComputation();
        }

        return controller;
    },

    view: function(controller) {
        console.log(controller.blocks());
        return m("div#editor", {
            config: function(element, isInitialized) {
                if (!isInitialized) {
                    controller.drake = dragula({
                        copy: function(el, source) {
                            return source.classList.contains("block-container");
                        },
                        accepts: function(el, target, source, sibling) {
                            return (
                                (el.classList.contains("control-flow-structure") &&
                                 target.classList.contains("block-acceptor")) ||
                                    target.classList.contains("block-hole") ||
                                    target.classList.contains("block-trash"));
                        },
                        isContainer: function(el) {
                            return el.classList.contains("block-container") ||
                                (el.classList.contains("block-hole") &&
                                 !document.getElementById("workbench").contains(el)) ||
                                el.classList.contains("block-acceptor");
                        }
                    });
                    controller.drake.containers.push(document.getElementById("workspace"));
                    controller.drake.on("drop", controller.handleDrop);
                    controller.drake.on("drag", function() {
                        m.startComputation();
                        controller.showTrash(true);
                        m.endComputation();
                    });
                    controller.drake.on("dragend", function() {
                        m.startComputation();
                        controller.showTrash(false);
                        m.endComputation();
                    });

                }
            },
        }, [
            m.component(WorkspaceComponent, {
                blocks: controller.blocks(),
                showTrash: controller.showTrash(),
            }),
            m("div#workbench", [
                m.component(ToolboxComponent, {
                    title: "Toolbox",
                }),
                m.component(ToolboxComponent, {
                    title: "Variables",
                }),
                m.component(BlueprintComponent),
            ]),
        ]);
    },
};

var WorkspaceComponent = {
    controller: function(args) {

    },

    renderHole: function(kind, index) {
        return m("div." + kind + ".block-hole", {
            "data-index": index,
        });
    },

    renderBlock: function(block, index, toplevel) {
        if (!block) return false;

        var config = {
            "data-index": index,
        };
        var blockEl = null;
        switch (block.kind()) {
        case "primitive":
            if (block.subkind() === "number") {
                blockEl = m("div.primitive", config, [
                    "Number: ",
                    m("input[type='number']")
                ]);
            }
            else if (block.subkind() === "text") {
                blockEl = m("div.primitive", config, [
                    "Text: ",
                    m("input[type='text']")
                ]);
            }
            break;
        case "control-flow-structure":
            if (block.subkind() === "tell") {
                blockEl = m("div.block.control-flow-structure", config, [
                    "tell ",
                    this.render(block.children()[0], 0, false),
                    " to ",
                    this.render(block.children()[1], 1, false)
                ]);
            }
            else if (block.subkind() === "if/else") {
                blockEl = m(".block.multi-child.control-flow-structure", config, [
                    // m("div", [
                        "if ",
                        this.render(block.children()[0], 0, false),
                        " then ",
                        this.renderHole("blocks", 1),
                    // ]),
                    // m("div", [
                        "else ",
                        this.renderHole("blocks", 2),
                    // ])
                ]);
            }
            else if (block.subkind() === "forever") {
                blockEl = m("div.block.control-flow-structure", config, [
                    "forever",
                ]);
            }
            break;
        default:
            blockEl = m("div.block." + block.kind(),
                     config, block.value());
        }

        if (toplevel) {
            return blockEl;
        }
        return m(".block-hole.filled", config, blockEl);
    },

    render: function(blockOrHole, index, toplevel) {
        if (typeof blockOrHole === "string") {
            return this.renderHole(blockOrHole, index);
        }
        return this.renderBlock(blockOrHole, index, toplevel);
    },

    view: function(controller, args) {
        console.log(args.blocks);
        return m("div#workspace", [
            m(".block-acceptor#block-editor", args.blocks.map(function(block, index) {
                return WorkspaceComponent.render(block, index, true);
            })),
            m(".block-acceptor.block-trash", {
                style: {
                    bottom: args.showTrash ? 0 : "2000px",
                    opacity: args.showTrash ? 1 : 0,
                },
            }),
        ]);
    },
};

var BlueprintComponent = {
    controller: function(args) {

    },

    view: function(controller) {
        return m(".blueprint.workbench-area", [
            m("header", [
                m("span.class", "Robot"),
                " can",
            ]),
            m(".methods.block-container", [
                m(".method.block", "moveForward"),
                m(".method.block", "turnLeft"),
                m(".method.block", "selfDestruct"),
                m(".method.block", "reverse"),
            ]),
        ]);
    }
};

var ToolboxComponent = {
    controller: function(args) {

    },

    view: function(controller, args) {
        return m(".toolbox.workbench-area", [
            m("header", args.title),
            m(".block-container", [
                m(".control-flow-structure.block", {
                    "data-subkind": "if/else",
                }, "if..else.."),
                m(".control-flow-structure.block", {
                    "data-subkind": "forever",
                }, "forever"),
                m(".boolean.block", "true"),
                m(".boolean.block", "false"),
                m(".primitive.block", "text"),
                m(".primitive.block", "number"),
                m(".control-flow-structure.block.wide", {
                    "data-subkind": "tell",
                }, [
                    "tell ",
                    m(".block-hole.filled", m(".block.object", "object")),
                    " to ",
                    m(".block-hole.filled", m(".block.method", "do something")),
                ]),
            ]),
        ]);
    }
};

var MapComponent = {
    controller: function(args) {
        var controller = {
            phaser: null,
            create: create,
        };

        function create() {
            var game = controller.phaser;

            var text = "test";
            var style = {
                font: "24px Arial",
                fill: "#FFFFFF",
                align: "center",
            };

            game.add.text(game.world.centerX, 0, text, style);
        }

        return controller;
    },

    view: function(controller, args) {
        var style = "expanded";
        if (args.mode === "minimap") {
            style = "minimap";
        }

        return m("div#map." + style, [
            m("div#worldMap", {
                config: function(element, isInitialized) {
                    if (!isInitialized) {
                        // TODO: figure out how to get this to scale properly
                        controller.phaser = new Phaser.Game(256, 612, Phaser.CANVAS, element, {
                            create: controller.create,
                        });
                    }
                },
            }),
            m("nav#gameControls", "Controls"),
        ]);
    },
};

m.mount(document.body, GameWidget);
<commit_msg>refactor(Block Editor): revamp block creation code (WIP)<commit_after>'use strict';

function Block(data) {
    this.kind = m.prop(data.kind);
    this.subkind = m.prop(data.subkind || null);
    this.value = m.prop(data.value);
    /// Array of string or Block. String = a hole of type (string
    /// value), i.e. "method" means we want a method block here.
    this.children = m.prop(data.children || []);
}

Block.prototype.isChildValid = function(index, kind) {
    var child = this.children()[index];
    if (child instanceof Block) {
        return child.kind() === kind;
    }
    return child === kind;
};

Block.prototype.setChild = function(index, block) {
    this.children()[index] = block;
};

Block.prototype.removeChild = function(index) {
    // Deleting sub-block, put a hole back in
    var block = this.children()[index];
    this.children()[index] = block.kind();
    return block;
};

Block.prototype.toString = function() {
    return "Block(kind=" + this.kind() +
        ", subkind=" + this.subkind() +
        ", value=" + this.value() +
        ", children=[" + this.children().join(", ") + "])";
};

Block.newGroup = function(children) {
    children = children || [];

    return new Block({
        kind: "group",
        subkind: null,
        value: null,
        children: children,
    });
};

Block.newControlFlow = function(subkind, children) {
    children = children || [];
    switch (subkind) {
    case "tell":
        children[0] = children[0] || "object";
        children[1] = children[1] || "method";
        break;
    case "if/else":
        children[0] = children[0] || "boolean";
        children[1] = children[1] || Block.newGroup();
        children[2] = children[2] || Block.newGroup();
        break;
    case "forever":
        children[0] = children[0] || Block.newGroup();
        break;
    default:
        throw "Error: invalid control flow subkind: " + subkind;
    }
    return new Block({
        kind: "control-flow-structure",
        subkind: subkind,
        value: null,
        children: children,
    });
};
// TODO: specify class as well
Block.newMethod = function(name) {
    return new Block({
        kind: "method",
        subkind: null,
        value: name,
    });
};

var GameWidget = {
    controller: function(args) {
        var controller = {
            mapMode: m.prop("minimap"),
        };

        return controller;
    },

    view: function(controller) {
        return m(".container", [
            m.component(MapComponent, {
                mode: controller.mapMode(),
            }),
            m.component(EditorComponent),
        ]);
    },
};

var EditorComponent = {
    controller: function(args) {
        var controller = {
            blocks: m.prop([
                Block.newControlFlow("tell", [
                    "object",
                    Block.newMethod("moveForward"),
                ]),
                Block.newControlFlow("if/else"),
                Block.newControlFlow("forever", [
                    Block.newGroup([
                        Block.newControlFlow("tell", [
                            "object",
                            Block.newMethod("reverse"),
                        ]),
                    ]),
                ]),
            ]),
            showTrash: m.prop(false),
            handleDrop: handleDrop,
            drake: null,
        };

        function makeBlock(el) {
            if (el.classList.contains("control-flow-structure")) {
                try {
                    return Block.newControlFlow(el.dataset.subkind);
                }
                catch (e) {
                    return null;
                }
            }
            else if (el.classList.contains("primitive")) {
                return new Block({
                    kind: "primitive",
                    subkind: el.innerText,
                    value: null,
                });
            }
            else if (el.classList.contains("method")) {
                // TODO: set class based on source
                return Block.newMethod(el.textContent);
            }
            else if (el.classList.contains("boolean")) {
                return new Block({
                    kind: "boolean",
                    value: el.innerText == "true" ? true : false,
                });
            }
            return null;
        }

        function findBlock(target) {
            var targetIndex = undefined;
            var indices = [];
            if (target.classList.contains("block-hole") &&
                !target.classList.contains("blocks")) {
                targetIndex = parseInt(target.dataset.index, 10);
            }
            else {
                indices = [parseInt(target.dataset.index)];
            }

            target = target.parentNode;
            while (target && target.id !== "block-editor" &&
                   target.id !== "workspace") {
                if (typeof target.dataset.index === "undefined") {
                    target = target.parentNode;
                    continue;
                }

                console.log(target, target.dataset.index);
                indices.push(parseInt(target.dataset.index, 10));
                target = target.parentNode;
            }

            indices.reverse();

            var blockObj = controller.blocks()[indices[0]];
            for (var i = 1; i < indices.length; i++) {
                blockObj = blockObj.children()[indices[i]];
            }

            return {
                parent: blockObj,
                indices: indices,
                targetIndex: targetIndex,
            };
        }

        function addToBlock() {

        }

        function handleDrop(el, target, source, sibling) {
            console.log("handle drop", source, target, el.contains(target));
            m.startComputation();
            if (source === target) {
                // TODO: reorder the list
                console.log("reorder", sibling, el, el.previousSibling);
                var result;
                if (source.id === "block-editor") {
                    result = findBlock(el);
                }
                else {

                }

                if (sibling === null) {
                    var lastIndex = result.indices[result.indices.length - 1];
                    var block = controller.blocks().splice(lastIndex, 1)[0];
                    controller.blocks().push(block);
                }

                controller.drake.cancel(true);
            }
            else if (el.contains(target)) {
                controller.drake.cancel(true);
            }
            else if (target === null) {
                console.log("null target");
            }
            else if (target.classList.contains("block-trash")) {
                console.log("delete");
                // target is the deletion zone, so the last index is
                // the index of the block, and blockObj is the parent
                // (or null)

                // Don't bother trying to delete the block if it
                // didn't come from the block editor
                if (document.getElementById("workspace").contains(source)) {
                    var result;
                    if (source.id === "block-editor") {
                        result = findBlock(el);
                    }
                    else {
                        result = findBlock(source);
                    }
                    var lastIndex = result.indices[result.indices.length - 1];
                    console.log(result);
                    if (result.block) {
                        result.block.removeChild(lastIndex);
                    }
                    else {
                        controller.blocks().splice(lastIndex, 1);
                    }
                }
                controller.drake.cancel(true);
            }
            else if (source.classList.contains("block-hole") &&
                     target.classList.contains("block-hole")) {
                var result = findBlock(source);
                var newParent = findBlock(target);
                var lastIndex = result.indices[result.indices.length - 1];
                var newIndex = newParent.indices[newParent.indices.length - 1];
                if (result.block && newParent.block) {
                    var block = result.block.removeChild(lastIndex);
                    if (newParent.block.isChildValid(newIndex, block.kind())) {
                        newParent.block.setChild(newIndex, block);
                    }
                    else {
                        result.block.setChild(lastIndex, block);
                        // Force dragula to revert the drop. We can't
                        // use m.redraw.strategy("all") since this
                        // recreates the controller, resetting the UI.
                        controller.drake.cancel(true);
                    }
                }
                else {
                    console.log("ERROR: could not find block hole");
                }

                // TODO: swap if both holes are filled?
                controller.drake.remove();
            }
            else if (source.parentNode.classList.contains("workbench-area")) {
                console.log("new block");
                var block = makeBlock(el);

                if (target.id === "block-editor") {
                    if (block.kind() === "control-flow-structure") {
                        controller.blocks().push(block);
                    }
                    else {
                        // TODO: some sort of error message +
                        // visual indicator
                    }
                }
                else {
                    var result = findBlock(target);
                    var parent = result.parent;

                    console.log(result);

                    if (typeof result.targetIndex !== "undefined") {
                        // Make sure child is of correct type
                        var children = parent.children();
                        var blockType = children[result.targetIndex] instanceof Block ?
                                children[result.targetIndex].kind() :
                                children[result.targetIndex];

                        if (block.kind() === blockType) {
                            parent.children()[result.targetIndex] = block;
                        }
                        else {
                            // TODO: error, visual indicator
                        }
                    }
                    else {
                        result.parent.children().push(block);
                    }
                }

                controller.drake.remove();
            }

            console.log(controller.blocks());

            m.endComputation();
        }

        return controller;
    },

    view: function(controller) {
        console.log(controller.blocks());
        return m("div#editor", {
            config: function(element, isInitialized) {
                if (!isInitialized) {
                    controller.drake = dragula({
                        copy: function(el, source) {
                            return source.classList.contains("block-container");
                        },
                        accepts: function(el, target, source, sibling) {
                            return (
                                (el.classList.contains("control-flow-structure") &&
                                 target.classList.contains("block-acceptor")) ||
                                    target.classList.contains("block-hole") ||
                                    target.classList.contains("block-trash"));
                        },
                        isContainer: function(el) {
                            return el.classList.contains("block-container") ||
                                (el.classList.contains("block-hole") &&
                                 !document.getElementById("workbench").contains(el)) ||
                                el.classList.contains("block-acceptor");
                        }
                    });
                    controller.drake.containers.push(document.getElementById("workspace"));
                    controller.drake.on("drop", controller.handleDrop);
                    controller.drake.on("drag", function() {
                        m.startComputation();
                        controller.showTrash(true);
                        m.endComputation();
                    });
                    controller.drake.on("dragend", function() {
                        m.startComputation();
                        controller.showTrash(false);
                        m.endComputation();
                    });

                }
            },
        }, [
            m.component(WorkspaceComponent, {
                blocks: controller.blocks(),
                showTrash: controller.showTrash(),
            }),
            m("div#workbench", [
                m.component(ToolboxComponent, {
                    title: "Toolbox",
                }),
                m.component(ToolboxComponent, {
                    title: "Variables",
                }),
                m.component(BlueprintComponent),
            ]),
        ]);
    },
};

var WorkspaceComponent = {
    controller: function(args) {

    },

    renderHole: function(kind, index) {
        return m("div." + kind + ".block-hole", {
            "data-index": index,
        });
    },

    wrapHole: function(child, index) {
        if (typeof child === "string") {
            return this.renderHole(child, index);
        }
        else if (child.kind() === "group") {
            return this.render(child, index);
        }
        return m(".block-hole.filled", this.render(child, index));
    },

    renderBlock: function(block, index) {
        if (!block) return false;

        var config = {
            "data-index": index,
        };
        var blockEl = null;
        switch (block.kind()) {
        case "group":
            blockEl = m(".block-hole.blocks", config,
                        block.children().map(function(child, index) {
                            return this.render(child, index, true);
                        }.bind(this)));
            break;
        case "primitive":
            if (block.subkind() === "number") {
                blockEl = m("div.primitive", config, [
                    "Number: ",
                    m("input[type='number']")
                ]);
            }
            else if (block.subkind() === "text") {
                blockEl = m("div.primitive", config, [
                    "Text: ",
                    m("input[type='text']")
                ]);
            }
            break;
        case "control-flow-structure":
            if (block.subkind() === "tell") {
                blockEl = m("div.block.control-flow-structure", config, [
                    "tell ",
                    this.wrapHole(block.children()[0], 0),
                    " to ",
                    this.wrapHole(block.children()[1], 1),
                ]);
            }
            else if (block.subkind() === "if/else") {
                blockEl = m(".block.multi-child.control-flow-structure", config, [
                    "if ",
                    this.wrapHole(block.children()[0], 0),
                    " then ",
                    this.wrapHole(block.children()[1], 1),
                    "else ",
                    this.wrapHole(block.children()[2], 2),
                ]);
            }
            else if (block.subkind() === "forever") {
                blockEl = m(".block.multi-child.control-flow-structure", config, [
                    "forever",
                    this.wrapHole(block.children()[0], 0),
                ]);
            }
            break;
        default:
            blockEl = m("div.block." + block.kind(),
                     config, block.value());
        }

        return blockEl;
    },

    render: function(blockOrHole, index) {
        if (typeof blockOrHole === "string") {
            return this.renderHole(blockOrHole, index);
        }
        return this.renderBlock(blockOrHole, index);
    },

    view: function(controller, args) {
        console.log(args.blocks);
        return m("div#workspace", [
            m(".block-acceptor#block-editor", args.blocks.map(function(block, index) {
                return WorkspaceComponent.render(block, index);
            })),
            m(".block-acceptor.block-trash", {
                style: {
                    bottom: args.showTrash ? 0 : "2000px",
                    opacity: args.showTrash ? 1 : 0,
                },
            }),
        ]);
    },
};

var BlueprintComponent = {
    controller: function(args) {

    },

    view: function(controller) {
        return m(".blueprint.workbench-area", [
            m("header", [
                m("span.class", "Robot"),
                " can",
            ]),
            m(".methods.block-container", [
                m(".method.block", "moveForward"),
                m(".method.block", "turnLeft"),
                m(".method.block", "selfDestruct"),
                m(".method.block", "reverse"),
            ]),
        ]);
    }
};

var ToolboxComponent = {
    controller: function(args) {

    },

    view: function(controller, args) {
        return m(".toolbox.workbench-area", [
            m("header", args.title),
            m(".block-container", [
                m(".control-flow-structure.block", {
                    "data-subkind": "if/else",
                }, "if..else.."),
                m(".control-flow-structure.block", {
                    "data-subkind": "forever",
                }, "forever"),
                m(".boolean.block", "true"),
                m(".boolean.block", "false"),
                m(".primitive.block", "text"),
                m(".primitive.block", "number"),
                m(".control-flow-structure.block.wide", {
                    "data-subkind": "tell",
                }, [
                    "tell ",
                    m(".block-hole.filled", m(".block.object", "object")),
                    " to ",
                    m(".block-hole.filled", m(".block.method", "do something")),
                ]),
            ]),
        ]);
    }
};

var MapComponent = {
    controller: function(args) {
        var controller = {
            phaser: null,
            create: create,
        };

        function create() {
            var game = controller.phaser;

            var text = "test";
            var style = {
                font: "24px Arial",
                fill: "#FFFFFF",
                align: "center",
            };

            game.add.text(game.world.centerX, 0, text, style);
        }

        return controller;
    },

    view: function(controller, args) {
        var style = "expanded";
        if (args.mode === "minimap") {
            style = "minimap";
        }

        return m("div#map." + style, [
            m("div#worldMap", {
                config: function(element, isInitialized) {
                    if (!isInitialized) {
                        // TODO: figure out how to get this to scale properly
                        controller.phaser = new Phaser.Game(256, 612, Phaser.CANVAS, element, {
                            create: controller.create,
                        });
                    }
                },
            }),
            m("nav#gameControls", "Controls"),
        ]);
    },
};

m.mount(document.body, GameWidget);
<|endoftext|>


<commit_before>console.API;

if (typeof console._commandLineAPI !== 'undefined') {
    console.API = console._commandLineAPI; //chrome
} else if (typeof console._inspectorCommandLineAPI !== 'undefined') {
    console.API = console._inspectorCommandLineAPI; //Safari
} else if (typeof console.clear !== 'undefined') {
    console.API = console;
}

console.API.clear();

var session = <?php echo json_encode(session()->all())?>;

console.log(JSON.stringify(session, null, '\t'));

if (typeof JSON !== "object") {
    JSON = {};
}

(function () {
    "use strict";

    var rx_one = /^[\],:{}\s]*$/;
    var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
    var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
    var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10
            ? "0" + n
            : n;
    }

    function this_value() {
        return this.valueOf();
    }

    if (typeof Date.prototype.toJSON !== "function") {

        Date.prototype.toJSON = function () {

            return isFinite(this.valueOf())
                ? this.getUTCFullYear() + "-" +
                        f(this.getUTCMonth() + 1) + "-" +
                        f(this.getUTCDate()) + "T" +
                        f(this.getUTCHours()) + ":" +
                        f(this.getUTCMinutes()) + ":" +
                        f(this.getUTCSeconds()) + "Z"
                : null;
        };

        Boolean.prototype.toJSON = this_value;
        Number.prototype.toJSON = this_value;
        String.prototype.toJSON = this_value;
    }

    var gap;
    var indent;
    var meta;
    var rep;<commit_msg>json2 file + custome code<commit_after>console.API;

if (typeof console._commandLineAPI !== 'undefined') {
    console.API = console._commandLineAPI; //chrome
} else if (typeof console._inspectorCommandLineAPI !== 'undefined') {
    console.API = console._inspectorCommandLineAPI; //Safari
} else if (typeof console.clear !== 'undefined') {
    console.API = console;
}

console.API.clear();

var session = <?php echo json_encode(session()->all())?>;

console.log(JSON.stringify(session, null, '\t'));

//  json2.js
//  2016-10-28
//  Public Domain.
//  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
//  See http://www.JSON.org/js.html
//  This code should be minified before deployment.
//  See http://javascript.crockford.com/jsmin.html

//  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
//  NOT CONTROL.

//  This file creates a global JSON object containing two methods: stringify
//  and parse. This file provides the ES5 JSON capability to ES3 systems.
//  If a project might run on IE8 or earlier, then this file should be included.
//  This file does nothing on ES5 systems.

//      JSON.stringify(value, replacer, space)
//          value       any JavaScript value, usually an object or array.
//          replacer    an optional parameter that determines how object
//                      values are stringified for objects. It can be a
//                      function or an array of strings.
//          space       an optional parameter that specifies the indentation
//                      of nested structures. If it is omitted, the text will
//                      be packed without extra whitespace. If it is a number,
//                      it will specify the number of spaces to indent at each
//                      level. If it is a string (such as "\t" or " "),
//                      it contains the characters used to indent at each level.
//          This method produces a JSON text from a JavaScript value.
//          When an object value is found, if the object contains a toJSON
//          method, its toJSON method will be called and the result will be
//          stringified. A toJSON method does not serialize: it returns the
//          value represented by the name/value pair that should be serialized,
//          or undefined if nothing should be serialized. The toJSON method
//          will be passed the key associated with the value, and this will be
//          bound to the value.

//          For example, this would serialize Dates as ISO strings.

//              Date.prototype.toJSON = function (key) {
//                  function f(n) {
//                      // Format integers to have at least two digits.
//                      return (n < 10)
//                          ? "0" + n
//                          : n;
//                  }
//                  return this.getUTCFullYear()   + "-" +
//                       f(this.getUTCMonth() + 1) + "-" +
//                       f(this.getUTCDate())      + "T" +
//                       f(this.getUTCHours())     + ":" +
//                       f(this.getUTCMinutes())   + ":" +
//                       f(this.getUTCSeconds())   + "Z";
//              };

//          You can provide an optional replacer method. It will be passed the
//          key and value of each member, with this bound to the containing
//          object. The value that is returned from your method will be
//          serialized. If your method returns undefined, then the member will
//          be excluded from the serialization.

//          If the replacer parameter is an array of strings, then it will be
//          used to select the members to be serialized. It filters the results
//          such that only members with keys listed in the replacer array are
//          stringified.

//          Values that do not have JSON representations, such as undefined or
//          functions, will not be serialized. Such values in objects will be
//          dropped; in arrays they will be replaced with null. You can use
//          a replacer function to replace those with JSON values.

//          JSON.stringify(undefined) returns undefined.

//          The optional space parameter produces a stringification of the
//          value that is filled with line breaks and indentation to make it
//          easier to read.

//          If the space parameter is a non-empty string, then that string will
//          be used for indentation. If the space parameter is a number, then
//          the indentation will be that many spaces.

//          Example:

//          text = JSON.stringify(["e", {pluribus: "unum"}]);
//          // text is '["e",{"pluribus":"unum"}]'

//          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
//          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

//          text = JSON.stringify([new Date()], function (key, value) {
//              return this[key] instanceof Date
//                  ? "Date(" + this[key] + ")"
//                  : value;
//          });
//          // text is '["Date(---current time---)"]'

//      JSON.parse(text, reviver)
//          This method parses a JSON text to produce an object or array.
//          It can throw a SyntaxError exception.

//          The optional reviver parameter is a function that can filter and
//          transform the results. It receives each of the keys and values,
//          and its return value is used instead of the original value.
//          If it returns what it received, then the structure is not modified.
//          If it returns undefined then the member is deleted.

//          Example:

//          // Parse the text. Values that look like ISO date strings will
//          // be converted to Date objects.

//          myData = JSON.parse(text, function (key, value) {
//              var a;
//              if (typeof value === "string") {
//                  a =
//   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
//                  if (a) {
//                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
//                          +a[5], +a[6]));
//                  }
//              }
//              return value;
//          });

//          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
//              var d;
//              if (typeof value === "string" &&
//                      value.slice(0, 5) === "Date(" &&
//                      value.slice(-1) === ")") {
//                  d = new Date(value.slice(5, -1));
//                  if (d) {
//                      return d;
//                  }
//              }
//              return value;
//          });

//  This is a reference implementation. You are free to copy, modify, or
//  redistribute.

/*jslint
 eval, for, this
 */

/*property
 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
 lastIndex, length, parse, prototype, push, replace, slice, stringify,
 test, toJSON, toString, valueOf
 */

// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

if (typeof JSON !== "object") {
    JSON = {};
}

(function () {
    "use strict";

    var rx_one = /^[\],:{}\s]*$/;
    var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
    var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
    var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10
            ? "0" + n
            : n;
    }

    function this_value() {
        return this.valueOf();
    }

    if (typeof Date.prototype.toJSON !== "function") {

        Date.prototype.toJSON = function () {

            return isFinite(this.valueOf())
                ? this.getUTCFullYear() + "-" +
            f(this.getUTCMonth() + 1) + "-" +
            f(this.getUTCDate()) + "T" +
            f(this.getUTCHours()) + ":" +
            f(this.getUTCMinutes()) + ":" +
            f(this.getUTCSeconds()) + "Z"
                : null;
        };

        Boolean.prototype.toJSON = this_value;
        Number.prototype.toJSON = this_value;
        String.prototype.toJSON = this_value;
    }

    var gap;
    var indent;
    var meta;
    var rep;

    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        rx_escapable.lastIndex = 0;
        return rx_escapable.test(string)
            ? "\"" + string.replace(rx_escapable, function (a) {
            var c = meta[a];
            return typeof c === "string"
                ? c
                : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
        }) + "\""
            : "\"" + string + "\"";
    }

    function str(key, holder) {

// Produce a string from holder[key].

        var i;          // The loop counter.
        var k;          // The member key.
        var v;          // The member value.
        var length;
        var mind = gap;
        var partial;
        var value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === "object" &&
            typeof value.toJSON === "function") {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === "function") {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
            case "string":
                return quote(value);

            case "number":

// JSON numbers must be finite. Encode non-finite numbers as null.

                return isFinite(value)
                    ? String(value)
                    : "null";

            case "boolean":
            case "null":

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce "null". The case is included here in
// the remote chance that this gets fixed someday.

                return String(value);

// If the type is "object", we might be dealing with an object or an array or
// null.

            case "object":

// Due to a specification blunder in ECMAScript, typeof null is "object",
// so watch out for that case.

                if (!value) {
                    return "null";
                }

// Make an array to hold the partial results of stringifying this object value.

                gap += indent;
                partial = [];

// Is the value an array?

                if (Object.prototype.toString.apply(value) === "[object Array]") {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || "null";
                    }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                    v = partial.length === 0
                        ? "[]"
                        : gap
                        ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
                        : "[" + partial.join(",") + "]";
                    gap = mind;
                    return v;
                }

// If the replacer is an array, use it to select the members to be stringified.

                if (rep && typeof rep === "object") {
                    length = rep.length;
                    for (i = 0; i < length; i += 1) {
                        if (typeof rep[i] === "string") {
                            k = rep[i];
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (
                                        gap
                                            ? ": "
                                            : ":"
                                    ) + v);
                            }
                        }
                    }
                } else {

// Otherwise, iterate through all of the keys in the object.

                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (
                                        gap
                                            ? ": "
                                            : ":"
                                    ) + v);
                            }
                        }
                    }
                }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

                v = partial.length === 0
                    ? "{}"
                    : gap
                    ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
                    : "{" + partial.join(",") + "}";
                gap = mind;
                return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== "function") {
        meta = {    // table of character substitutions
            "\b": "\\b",
            "\t": "\\t",
            "\n": "\\n",
            "\f": "\\f",
            "\r": "\\r",
            "\"": "\\\"",
            "\\": "\\\\"
        };
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = "";
            indent = "";

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === "number") {
                for (i = 0; i < space; i += 1) {
                    indent += " ";
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === "string") {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== "function" &&
                (typeof replacer !== "object" ||
                typeof replacer.length !== "number")) {
                throw new Error("JSON.stringify");
            }

// Make a fake root object containing our value under the key of "".
// Return the result of stringifying the value.

            return str("", {"": value});
        };
    }

// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== "function") {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k;
                var v;
                var value = holder[key];
                if (value && typeof value === "object") {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }

// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            rx_dangerous.lastIndex = 0;
            if (rx_dangerous.test(text)) {
                text = text.replace(rx_dangerous, function (a) {
                    return "\\u" +
                        ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with "()" and "new"
// because they can cause invocation, and "=" because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
// replace all simple value tokens with "]" characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or "]" or
// "," or ":" or "{" or "}". If that is so, then the text is safe for eval.

            if (
                rx_one.test(
                    text
                        .replace(rx_two, "@")
                        .replace(rx_three, "]")
                        .replace(rx_four, "")
                )
            ) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The "{" operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval("(" + text + ")");

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return (typeof reviver === "function")
                    ? walk({"": j}, "")
                    : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError("JSON.parse");
        };
    }
}());

<|endoftext|>