<commit_before>export const NodeTypes = {
  BLOCKQUOTE: "BLOCKQUOTE",
  FRAGMENT: "FRAGMENT",
  FRAGMENT_EM: "FRAGMENT_EM",
  FRAGMENT_CODE: "FRAGMENT_CODE",
  P: "P",
  H1: "H1",
  H2: "H2",
  H3: "H3",
  H4: "H4",
  H5: "H5",
  H6: "H6",
  LINK: "LINK",
  LIST_ORDERED: "LIST_ORDERED",
  LIST_UNORDERED: "LIST_UNORDERED",
  LIST_ITEM: "LIST_ITEM",
  IMAGE: "IMAGE",
  CODE: "CODE"
};

function parsePortableTextNodeType(portableTextNode) {
  if (portableTextNode.customType === "bullet") {
    return NodeTypes.LIST_UNORDERED;
  }

  if (portableTextNode.customType === "number") {
    return NodeTypes.LIST_ORDERED;
  }

  if (portableTextNode._type === "block") {
    if (portableTextNode.style === "h1") {
      return NodeTypes.H1;
    }

    if (portableTextNode.style === "blockquote") {
      return NodeTypes.BLOCKQUOTE;
    }

    if (portableTextNode.listItem) {
      return NodeTypes.LIST_ITEM;
    }

    return NodeTypes.P;
  }

  if (portableTextNode._type === "mainImage") {
    return NodeTypes.IMAGE;
  }

  if (portableTextNode._type === "code") {
    return NodeTypes.CODE;
  }

  return NodeTypes.FRAGMENT;
}

function parsePortableTextNodeAttrs(portableTextNode, markDefs) {
  let nodeType = parsePortableTextNodeType(portableTextNode);

  let attrs = {};
  if (nodeType === NodeTypes.FRAGMENT) {
    attrs.text = portableTextNode.text;
  }

  if (portableTextNode.marks && portableTextNode.marks.length > 0) {
    if (portableTextNode.marks[0] == "em") {
      nodeType = NodeTypes.FRAGMENT_EM;
    } else if (portableTextNode.marks[0] == "code") {
      nodeType = NodeTypes.FRAGMENT_CODE;
    } else {
      const markId = portableTextNode.marks[0];
      const linkAttrs = markDefs[markId];
      nodeType = NodeTypes.LINK;
      attrs.href = linkAttrs.href;
    }
  }

  if (nodeType === NodeTypes.IMAGE) {
    attrs.alt = portableTextNode.alt;
    attrs.caption = portableTextNode.caption;
    attrs.metadata = portableTextNode.metadata;
  }

  if (nodeType === NodeTypes.CODE) {
    attrs.language = portableTextNode.language;
    attrs.markup = portableTextNode.markup;
  }

  return { type: nodeType, ...attrs };
}

function walkNodes(nodes, cb) {
  if (Array.isArray(nodes)) {
    return nodes.forEach(n => {
      walkNodes(n, cb);
    });
  }

  if (nodes.children) {
    nodes.children.forEach(n => {
      walkNodes(n, cb);
    });
  }

  cb(nodes);
}

export function getAllMarks(portableTextNode) {
  let marks = {};
  walkNodes(portableTextNode, n => {
    if (n.markDefs) {
      n.markDefs.forEach(d => {
        marks[d._key] = d;
      });
    }
  });
  return marks;
}

export function nestLists(portableTextNode) {
  if (!Array.isArray(portableTextNode)) {
    return portableTextNode;
  }

  let grouped = [];
  let currentListGroup;
  portableTextNode.forEach(node => {
    if (node.listItem) {
      if (currentListGroup) {
        // Append to the existing group
        currentListGroup.children.push(node);
      } else {
        // Start a new group
        currentListGroup = {
          _type: "block",
          customType: node.listItem,
          children: [node]
        };
      }
    } else {
      if (currentListGroup) {
        // Reset the group
        grouped.push(currentListGroup);
        currentListGroup = undefined;
      }
      grouped.push(node);
    }
  });

  return grouped;
}

export function parsePortableText(portableTextNode, markDefs) {
  if (Array.isArray(portableTextNode)) {
    return portableTextNode.map(n => parsePortableText(n, markDefs));
  }

  if (portableTextNode.children) {
    return {
      ...parsePortableTextNodeAttrs(portableTextNode, markDefs),
      children: parsePortableText(portableTextNode.children, markDefs)
    };
  }

  return parsePortableTextNodeAttrs(portableTextNode, markDefs);
}
<commit_msg>Handle all heading sizes<commit_after>export const NodeTypes = {
  BLOCKQUOTE: "BLOCKQUOTE",
  FRAGMENT: "FRAGMENT",
  FRAGMENT_EM: "FRAGMENT_EM",
  FRAGMENT_CODE: "FRAGMENT_CODE",
  P: "P",
  H1: "H1",
  H2: "H2",
  H3: "H3",
  H4: "H4",
  H5: "H5",
  H6: "H6",
  LINK: "LINK",
  LIST_ORDERED: "LIST_ORDERED",
  LIST_UNORDERED: "LIST_UNORDERED",
  LIST_ITEM: "LIST_ITEM",
  IMAGE: "IMAGE",
  CODE: "CODE"
};

function parsePortableTextNodeType(portableTextNode) {
  if (portableTextNode.customType === "bullet") {
    return NodeTypes.LIST_UNORDERED;
  }

  if (portableTextNode.customType === "number") {
    return NodeTypes.LIST_ORDERED;
  }

  if (portableTextNode._type === "block") {
    if (portableTextNode.style === "h1") {
      return NodeTypes.H1;
    }

    if (portableTextNode.style === "h2") {
      return NodeTypes.H2;
    }

    if (portableTextNode.style === "h3") {
      return NodeTypes.H3;
    }

    if (portableTextNode.style === "h4") {
      return NodeTypes.H4;
    }

    if (portableTextNode.style === "h5") {
      return NodeTypes.H5;
    }

    if (portableTextNode.style === "h6") {
      return NodeTypes.H6;
    }

    if (portableTextNode.style === "blockquote") {
      return NodeTypes.BLOCKQUOTE;
    }

    if (portableTextNode.listItem) {
      return NodeTypes.LIST_ITEM;
    }

    return NodeTypes.P;
  }

  if (portableTextNode._type === "mainImage") {
    return NodeTypes.IMAGE;
  }

  if (portableTextNode._type === "code") {
    return NodeTypes.CODE;
  }

  return NodeTypes.FRAGMENT;
}

function parsePortableTextNodeAttrs(portableTextNode, markDefs) {
  let nodeType = parsePortableTextNodeType(portableTextNode);

  let attrs = {};
  if (nodeType === NodeTypes.FRAGMENT) {
    attrs.text = portableTextNode.text;
  }

  if (portableTextNode.marks && portableTextNode.marks.length > 0) {
    if (portableTextNode.marks[0] == "em") {
      nodeType = NodeTypes.FRAGMENT_EM;
    } else if (portableTextNode.marks[0] == "code") {
      nodeType = NodeTypes.FRAGMENT_CODE;
    } else {
      const markId = portableTextNode.marks[0];
      const linkAttrs = markDefs[markId];
      nodeType = NodeTypes.LINK;
      attrs.href = linkAttrs.href;
    }
  }

  if (nodeType === NodeTypes.IMAGE) {
    attrs.alt = portableTextNode.alt;
    attrs.caption = portableTextNode.caption;
    attrs.metadata = portableTextNode.metadata;
  }

  if (nodeType === NodeTypes.CODE) {
    attrs.language = portableTextNode.language;
    attrs.markup = portableTextNode.markup;
  }

  return { type: nodeType, ...attrs };
}

function walkNodes(nodes, cb) {
  if (Array.isArray(nodes)) {
    return nodes.forEach(n => {
      walkNodes(n, cb);
    });
  }

  if (nodes.children) {
    nodes.children.forEach(n => {
      walkNodes(n, cb);
    });
  }

  cb(nodes);
}

export function getAllMarks(portableTextNode) {
  let marks = {};
  walkNodes(portableTextNode, n => {
    if (n.markDefs) {
      n.markDefs.forEach(d => {
        marks[d._key] = d;
      });
    }
  });
  return marks;
}

export function nestLists(portableTextNode) {
  if (!Array.isArray(portableTextNode)) {
    return portableTextNode;
  }

  let grouped = [];
  let currentListGroup;
  portableTextNode.forEach(node => {
    if (node.listItem) {
      if (currentListGroup) {
        // Append to the existing group
        currentListGroup.children.push(node);
      } else {
        // Start a new group
        currentListGroup = {
          _type: "block",
          customType: node.listItem,
          children: [node]
        };
      }
    } else {
      if (currentListGroup) {
        // Reset the group
        grouped.push(currentListGroup);
        currentListGroup = undefined;
      }
      grouped.push(node);
    }
  });

  return grouped;
}

export function parsePortableText(portableTextNode, markDefs) {
  if (Array.isArray(portableTextNode)) {
    return portableTextNode.map(n => parsePortableText(n, markDefs));
  }

  if (portableTextNode.children) {
    return {
      ...parsePortableTextNodeAttrs(portableTextNode, markDefs),
      children: parsePortableText(portableTextNode.children, markDefs)
    };
  }

  return parsePortableTextNodeAttrs(portableTextNode, markDefs);
}
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var AbstractView = require("modules/view/AbstractView");
    var RadMapView = require("modules/view/rad/top/RadMapView");
    var RadClusterListView = require("modules/view/rad/top/RadClusterListView");
    var RadiationClusterModel = require("modules/model/radiation/RadiationClusterModel");
    var RadiationClusterCollection = require("modules/collection/radiation/RadiationClusterCollection");
    var ModalRadiationListView = require("modules/view/rad/top/ModalRadiationListView");
    var FileAPIUtil = require("modules/util/FileAPIUtil");
    var CommonUtil = require("modules/util/CommonUtil");
    var vexDialog = require("vexDialog");

    /**
     * 放射線アプリのトップ画面を表示するためのViewクラスを作成する。
     *
     * @class 放射線アプリのトップ画面を表示するためのView
     * @exports RadTopView
     * @constructor
     */
    var RadTopView = AbstractView.extend({
        /**
         * このViewのテンプレートファイルパス
         */
        template : require("ldsh!templates/{mode}/top/top"),
        events : {
            "click [data-radiation-upload-button]" : "onClickRadiationUploadButton",
            "click [data-toggle-sidebar]" : "toggleSidebar",
            "sidebar.hide" : "hideSidebar",
            "sidebar.show" : "showSidebar"
        },

        /**
         * リストのスクロールオーバーレイ（上）が表示されているかどうか
         */
        isShowScrollUp : true,

        /**
         * リストのスクロールオーバーレイ（下）が表示されているかどうか
         */
        isShowScrollDown : true,

        /**
         * Viewの描画処理の開始前に呼び出されるコールバック関数。
         *

         * 記事一覧の表示処理を開始する。
         *

         * @memberOf RadTopView#
         */
        beforeRendered : function() {
            this.hideSidebar();
        },

        /**
         * Viewの描画処理の終了後に呼び出されるコールバック関数。
         * @memberOf RadTopView#
         */
        afterRendered : function() {
            $(".sidemenu-bottom__scroll")
                .on("scroll", this.onScrollSidebar.bind(this));
        },

        /**
         * 初期化
         * @memberOf RadTopView#
         */
        initialize : function() {
            // ローディングを開始
            this.showLoading();

            this.initCollection();
            this.initEvents();

            this.setView("#map-container", new RadMapView({
                radiationClusterCollection: this.radClusterCollection
            }));
            this.setView("#contents__secondary .sidebar", new RadClusterListView({
                collection: this.radClusterCollection
            }));

            // ローディングを停止
            //this.hideLoading();
        },
        /**
         * 線量データアップロードボタンが押下された際のコールバック
         * @memberOf RadTopView#
         */
        onClickRadiationUploadButton : function() {
            if (!!~navigator.userAgent.indexOf("namie-debug")) {
                // XXX: dummy
                this.setRadiationList([
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()}
                ]);
            } else if (CommonUtil.isCordova()) {
                this.showLoading();
                FileAPIUtil.getHoribaRadiationList(this.setRadiationList.bind(this));
            } else {
                alert("ご使用の端末ではアップロードできません。");
                return;
            }
        },
        /**
         * 画面に線量データ一覧を表示する関数
         * @param {Array} fileEntryArray FileEntryオブジェクトの配列
         * @memberOf RadTopView#
         */
        setRadiationList : function(fileEntryArray) {
            var urls = [];
            var fileCount = 0;
            if (!fileEntryArray || fileEntryArray.length === 0) {
                vexDialog.defaultOptions.className = 'vex-theme-default';
                vexDialog.alert("放射線量データがありません。");
                this.hideLoading();
                return;
            }
            var modalRadiationListView = new ModalRadiationListView();
            modalRadiationListView.fileEntryArray = fileEntryArray;

            this.setView("#radiation-list-container", modalRadiationListView);
            this.listenTo(modalRadiationListView, "closeModalRadiationList", function () {
                modalRadiationListView.remove();
                // URLを元に戻す
                app.router.back();
            });
            modalRadiationListView.render();

            // カレンダー画面用URLに遷移
            app.router.navigate("radiationList", {
                trigger: true,
                replace: false
            });
            $(".sidemenu-bottom__scroll").trigger("scroll");
            this.hideLoading();
        },

        /**
         * collectionを初期化する
         * @memberOf RadTopView#
         */
        initCollection : function () {
            this.radClusterCollection = new RadiationClusterCollection();
            // 自身のアップロードしたデータのみ検索
            this.radClusterCollection.setSearchConditionByMyself();
            this.radClusterCollection
                .fetch()
                .done(function (col) {
                    $(".sidemenu-bottom__scroll").trigger("scroll");
                    if (col.size() === 0) {
                        return;
                    }
                    col.each(function (model) {
                        model.set("hidden", true);
                    });
                    col.at(0).set("hidden", false);
                });
            // TODO: テスト用データの作成を差し替える
            //_(10).times($.proxy(function(index) {
            //    this.radClusterCollection.push(
            //        new RadiationClusterModel({
            //            __id: "test-content-" + index,
            //            dispTitle: "テスト用コンテンツ" + index
            //        })
            //    );
            //}), this);
        },

        /**
         * イベントを初期化する
         * @memberOf RadTopView#
         */
        initEvents : function() {
            this.listenTo(this.radClusterCollection, "sync", this.render);
        },

        /**
         * サイドバーを表示する
         * @memberOf RadTopView#
         */
        showSidebar : function () {
            $("#snap-content").removeClass("is-expanded");
            $(window).triggerHandler("resize");
        },

        /**
         * サイドバーを非表示にする
         * @memberOf RadTopView#
         */
        hideSidebar : function () {
            $("#snap-content").addClass("is-expanded");
            $(window).triggerHandler("resize");
        },

        /**
         * サイドバーの表示/非表示を切り替える
         * @memberOf RadTopView#
         */
        toggleSidebar : function () {
            $("#snap-content").toggleClass("is-expanded");
            $(window).triggerHandler("resize");
        },

        /**
         * サイドバーがスクロールされたら呼ばれる
         * @memberOf RadTopView#
         * @param {Event} ev
         */
        onScrollSidebar : _.throttle(function(ev) {
            var el = ev.currentTarget;
            var $container = $(el);
            var scrollTop = $container.scrollTop();
            var containerHeight = $container.height();
            var contentHeight = $container[0].scrollHeight;

            if (scrollTop > 0) {
                if(!this.isShowScrollUp){
                    this.isShowScrollUp = true;
                    $("#radiation-scrollUp").animate({
                        height : "50px",
                        opacity : 1
                    });
                }
            } else {
                if(this.isShowScrollUp){
                    this.isShowScrollUp = false;
                    $("#radiation-scrollUp").animate({
                        height : 0,
                        opacity : 0
                    });
                }
            }

            if (scrollTop < contentHeight - containerHeight) {
                if(!this.isShowScrolldown){
                    this.isShowScrolldown = true;
                    $("#radiation-scrollDown").animate({
                        height : "50px",
                        opacity : 1
                    });
                }
            } else {
                if(this.isShowScrolldown){
                    this.isShowScrolldown = false;
                    $("#radiation-scrollDown").animate({
                        height : 0,
                        opacity : 0
                    });
                }
            }
        }, 150)
    });

    module.exports = RadTopView;
});
<commit_msg>NAM-1147 放射線：トップ画面のサイドバー上下の矢印と、表示・非表示の矢印が同じ<commit_after>define(function(require, exports, module) {
    "use strict";

    var app = require("app");
    var AbstractView = require("modules/view/AbstractView");
    var RadMapView = require("modules/view/rad/top/RadMapView");
    var RadClusterListView = require("modules/view/rad/top/RadClusterListView");
    var RadiationClusterModel = require("modules/model/radiation/RadiationClusterModel");
    var RadiationClusterCollection = require("modules/collection/radiation/RadiationClusterCollection");
    var ModalRadiationListView = require("modules/view/rad/top/ModalRadiationListView");
    var FileAPIUtil = require("modules/util/FileAPIUtil");
    var CommonUtil = require("modules/util/CommonUtil");
    var vexDialog = require("vexDialog");

    /**
     * 放射線アプリのトップ画面を表示するためのViewクラスを作成する。
     *
     * @class 放射線アプリのトップ画面を表示するためのView
     * @exports RadTopView
     * @constructor
     */
    var RadTopView = AbstractView.extend({
        /**
         * このViewのテンプレートファイルパス
         */
        template : require("ldsh!templates/{mode}/top/top"),
        events : {
            "click [data-radiation-upload-button]" : "onClickRadiationUploadButton",
            "click [data-toggle-sidebar]" : "toggleSidebar",
            "sidebar.hide" : "hideSidebar",
            "sidebar.show" : "showSidebar",
            "click #radiation-scrollDown" : "scrollDown"
        },

        /**
         * リストのスクロールオーバーレイ（上）が表示されているかどうか
         */
        isShowScrollUp : true,

        /**
         * リストのスクロールオーバーレイ（下）が表示されているかどうか
         */
        isShowScrollDown : true,

        /**
         * Viewの描画処理の開始前に呼び出されるコールバック関数。
         *

         * 記事一覧の表示処理を開始する。
         *

         * @memberOf RadTopView#
         */
        beforeRendered : function() {
            this.hideSidebar();
        },

        /**
         * Viewの描画処理の終了後に呼び出されるコールバック関数。
         * @memberOf RadTopView#
         */
        afterRendered : function() {
            $(".sidemenu-bottom__scroll")
                .on("scroll", this.onScrollSidebar.bind(this));
        },

        /**
         * 初期化
         * @memberOf RadTopView#
         */
        initialize : function() {
            // ローディングを開始
            this.showLoading();

            this.initCollection();
            this.initEvents();

            this.setView("#map-container", new RadMapView({
                radiationClusterCollection: this.radClusterCollection
            }));
            this.setView("#contents__secondary .sidebar", new RadClusterListView({
                collection: this.radClusterCollection
            }));

            // ローディングを停止
            //this.hideLoading();
        },
        /**
         * 線量データアップロードボタンが押下された際のコールバック
         * @memberOf RadTopView#
         */
        onClickRadiationUploadButton : function() {
            if (!!~navigator.userAgent.indexOf("namie-debug")) {
                // XXX: dummy
                this.setRadiationList([
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()},
                    {name: "dummy" + Math.random(), lastModifiedDate: new Date()}
                ]);
            } else if (CommonUtil.isCordova()) {
                this.showLoading();
                FileAPIUtil.getHoribaRadiationList(this.setRadiationList.bind(this));
            } else {
                alert("ご使用の端末ではアップロードできません。");
                return;
            }
        },
        /**
         * 画面に線量データ一覧を表示する関数
         * @param {Array} fileEntryArray FileEntryオブジェクトの配列
         * @memberOf RadTopView#
         */
        setRadiationList : function(fileEntryArray) {
            var urls = [];
            var fileCount = 0;
            if (!fileEntryArray || fileEntryArray.length === 0) {
                vexDialog.defaultOptions.className = 'vex-theme-default';
                vexDialog.alert("放射線量データがありません。");
                this.hideLoading();
                return;
            }
            var modalRadiationListView = new ModalRadiationListView();
            modalRadiationListView.fileEntryArray = fileEntryArray;

            this.setView("#radiation-list-container", modalRadiationListView);
            this.listenTo(modalRadiationListView, "closeModalRadiationList", function () {
                modalRadiationListView.remove();
                // URLを元に戻す
                app.router.back();
            });
            modalRadiationListView.render();

            // カレンダー画面用URLに遷移
            app.router.navigate("radiationList", {
                trigger: true,
                replace: false
            });
            $(".sidemenu-bottom__scroll").trigger("scroll");
            this.hideLoading();
        },

        /**
         * collectionを初期化する
         * @memberOf RadTopView#
         */
        initCollection : function () {
            this.radClusterCollection = new RadiationClusterCollection();
            // 自身のアップロードしたデータのみ検索
            this.radClusterCollection.setSearchConditionByMyself();
            this.radClusterCollection
                .fetch()
                .done(function (col) {
                    $(".sidemenu-bottom__scroll").trigger("scroll");
                    if (col.size() === 0) {
                        return;
                    }
                    col.each(function (model) {
                        model.set("hidden", true);
                    });
                    col.at(0).set("hidden", false);
                });
            // TODO: テスト用データの作成を差し替える
            //_(10).times($.proxy(function(index) {
            //    this.radClusterCollection.push(
            //        new RadiationClusterModel({
            //            __id: "test-content-" + index,
            //            dispTitle: "テスト用コンテンツ" + index
            //        })
            //    );
            //}), this);
        },

        /**
         * イベントを初期化する
         * @memberOf RadTopView#
         */
        initEvents : function() {
            this.listenTo(this.radClusterCollection, "sync", this.render);
        },

        /**
         * サイドバーを表示する
         * @memberOf RadTopView#
         */
        showSidebar : function () {
            $("#snap-content").removeClass("is-expanded");
            $(window).triggerHandler("resize");
        },

        /**
         * サイドバーを非表示にする
         * @memberOf RadTopView#
         */
        hideSidebar : function () {
            $("#snap-content").addClass("is-expanded");
            $(window).triggerHandler("resize");
        },

        /**
         * サイドバーの表示/非表示を切り替える
         * @memberOf RadTopView#
         */
        toggleSidebar : function () {
            $("#snap-content").toggleClass("is-expanded");
            $(window).triggerHandler("resize");
        },

        /**
         * サイドバーがスクロールされたら呼ばれる
         * @memberOf RadTopView#
         * @param {Event} ev
         */
        onScrollSidebar : _.throttle(function(ev) {
            var el = ev.currentTarget;
            var $container = $(el);
            var scrollTop = $container.scrollTop();
            var containerHeight = $container.height();
            var contentHeight = $container[0].scrollHeight;

            if (scrollTop > 0) {
                if(!this.isShowScrollUp){
                    this.isShowScrollUp = true;
                    $("#radiation-scrollUp").animate({
                        height : "50px",
                        opacity : 1
                    });
                }
            } else {
                if(this.isShowScrollUp){
                    this.isShowScrollUp = false;
                    $("#radiation-scrollUp").animate({
                        height : 0,
                        opacity : 0
                    });
                }
            }

            if (scrollTop < contentHeight - containerHeight) {
                if(!this.isShowScrolldown){
                    this.isShowScrolldown = true;
                    $("#radiation-scrollDown").animate({
                        height : "50px",
                        opacity : 1
                    });
                }
            } else {
                if(this.isShowScrolldown){
                    this.isShowScrolldown = false;
                    $("#radiation-scrollDown").animate({
                        height : 0,
                        opacity : 0
                    });
                }
            }
        }, 150),

        /**
         * スクロールバー（下）が押されたら呼ばれる
         * @memberOf RadTopView#
         * @param {Event} ev
         */
        scrollDown : function(ev) {
            var $target = $(".sidemenu-bottom__scroll");
            var outerHeight = $(".rad-cluster-item").outerHeight();
            var scrollTop = $target.scrollTop();
            $target.animate({scrollTop : scrollTop + outerHeight});
        }
    });

    module.exports = RadTopView;
});
<|endoftext|>


<commit_before>'use strict';

describe('tvGuide.guide module', function() {

  beforeEach(module('tvGuide.guide'));

  describe('guide controller', function(){

  	var $scope, schedule, guideCtrl;

    beforeEach(inject(function($controller) {
      $scope = {};
      schedule = {};
      guideCtrl = $controller('GuideController', {$scope : $scope, schedule:schedule});
    }));

    it('should be defined', function() {
      expect(guideCtrl).toBeDefined();
    });

	it('schedue should be defined', function() {
      expect(schedule).toBeDefined();
    });
  });
});<commit_msg>Correcting test case<commit_after>'use strict';

describe('tvGuide.guide module', function() {

  beforeEach(module('tvGuide.guide'));

  describe('guide controller', function(){

  	var $scope, todaySchedule, guideCtrl;

    beforeEach(inject(function($controller) {
      $scope = {};
      todaySchedule = {};
      guideCtrl = $controller('GuideController', {$scope : $scope, todaySchedule:todaySchedule});
    }));

    it('should be defined', function() {
      expect(guideCtrl).toBeDefined();
    });

	it('schedue should be defined', function() {
      expect(todaySchedule).toBeDefined();
    });
  });
});<|endoftext|>


<commit_before>// datatables-tool.js

// When there are errors call this
var showAlert = function(title, message, level) {
	var $div;
	level = level || 0;
	$div = $("
").addClass("alert").text(message);
	$div.prepend('<button type="button" class="close" data-dismiss="alert">×</button>');
	$div.prepend("" + title + " ");
	if (level) {
		$div.addClass("alert-error");
	}
	return $div.prependTo("body");
};

var escapeSQL = function(column_name) {
	return "\"" + column_name + "\""
}

// Function to map JSON data between DataTables format and ScraperWiki's SQL endpoint format.
// It returns a function for the fnServerData parameter
var convertData = function(table_name, column_names) {
	// This is a wrapper round the GET request DataTables makes to get more data
	// sSource - the URL, we don't use it, we hard code it instead
	// aoData - contains the URL parameters, e.g. what page, what to filter, what order and so on
	// fnCallback - where to call with the data you get back
	// oSettings - settings object for the whole DataTables, see http://datatables.net/docs/DataTables/1.9.0/DataTable.models.oSettings.html
	return function ( sSource, aoData, fnCallback, oSettings ) {
		// convert aoData into a normal hash (called ps)
		var params = {}
		for (var i=0;i<aoData.length;i++) {
			params[aoData[i].name] = aoData[i].value
		}
		console.log("DataTables params:", params)

		// construct SQL query needed according to the parameters
		var columns  = _.map(column_names, escapeSQL).join(",")
		var order_by = ""
		if (params.iSortingCols >= 1) {
			var order_parts = []
			for (var i = 0; i < params.iSortingCols; i++) {
				order_part = escapeSQL(column_names[params["iSortCol_" + i]])
				if (params["sSortDir_" + i] == 'desc') {
					order_part += " desc"
				} else if (params["sSortDir_" + i] != 'asc') {
					showAlert("Got unknown sSortDir_" + i + " value in table " + table_name)
				}
				order_parts.push(order_part)
			}
			order_by = " order by " + order_parts.join(",")
		}
		var where = ""
		if (params.sSearch) {
			// XXX no idea if this bog standard Javascript escape really does what we want with SQL databases.
			// There's no security risk (as endpoint is sandboxed). There could be user experience pain though.
			var search = "'%" + escape(params.sSearch.toLowerCase()) + "%'"
			where = " where " + _.map(column_names, function(n) { return "lower(" + escapeSQL(n) + ") like " + search }).join(" or ")
		}
		var query = "select " + columns +
			     " from " + escapeSQL(table_name) +
				 where +
				 order_by +
			     " limit " + params.iDisplayLength +
			     " offset " + params.iDisplayStart
		console.log("SQL query:", query)

		// get column counts
		scraperwiki.sql("select (select count(*) from \"" + table_name + "\") as total, (select count(*) from " + table_name + where + ") as display_total", function (data) {
			var counts = data[0]

			oSettings.jqXHR = $.ajax( {
				"dataType": 'json',
				"type": "GET",
				"url": sqliteEndpoint,
				"data": { q: query },
				"success": function ( response ) {
					// ScraperWiki returns a list of dicts. This converts it to a list of lists.
					var rows = []
					for (var i=0;i<response.length;i++) {
						var row = []
						for (k in response[i]) {
							row.push(response[i][k])
						}
						rows.push(row)
					}
					// Send the data to dataTables
					fnCallback({
						"aaData" : rows,
						"iTotalRecords": data[0].total, // without filtering
						"iTotalDisplayRecords": data[0].display_total // after filtering
					})
				},
				"error": function(jqXHR, textStatus, errorThrown) {
					showAlert(errorThrown, jqXHR.responseText, "error")
				}
			} );
		}, function(jqXHR, textStatus, errorThrown) {
			showAlert(errorThrown, jqXHR.responseText, "error")
		})
	}
}

// Find the column names
function getTableColumnNames(table_name, callback){
    scraperwiki.sql("select * from \"" + table_name + "\" limit 1", function(data) {
		callback(_.keys(data[0]))
	}, function(jqXHR, textStatus, errorThrown) {
		showAlert(errorThrown, jqXHR.responseText, "error")
	})
}

// Make one of the DataTables (in one tab)
var constructDataTable = function(i, table_name) {
	// Find or make the table
	$(".maintable").hide()
	var id = "table_" + i
	var $outer = $("#" + id)
	if ($outer.length == 0) {
		console.log("making a new table:", table_name)
		$outer = $('

')
		$('body').append($outer)
	} else {
		$outer.show()
		console.log("reusing cached table:", table_name)
		return
	}
	var $t = $outer.find("table")

	// Find out the column names
	getTableColumnNames(table_name, function(column_names) {
		console.log("Columns", column_names)
		if (column_names.length == 0) {
			showAlert("No data in the table", jqXHR.responseText)
			return
		}

		// Make the column headings
        var thead = '

'
		_.each(column_names, function(column_name) {
			thead += '' + column_name + '	'
		})
		thead += '

'
		$t.append(thead)

		// Fill in the datatables object
		$t.dataTable( {
			"bProcessing": true,
			"bServerSide": true,
			"bDeferRender": true,
			"bPaginate": true,
			"sPaginationType": "full_numbers",
			"bFilter": true,
			"iDisplayLength": 100,
            "bScrollCollapse": true,
//            "sDom": '<"H"<"#schema_'+table_name+'">lfr>t<"F"ip>',
            "sDom": '<"H"fip>rt<"F"pil>',
			"aLengthMenu": [10, 100, 1000],
			"fnServerData": convertData(table_name, column_names),
            "fnRowCallback": function( tr, array, iDisplayIndex, iDisplayIndexFull ) {
                $('td', tr).each(function(){
                    $(this).html(
                        $(this).html().replace(
                            /((http|https|ftp):\/\/[a-zA-Z0-9-_~#:\.\?%&\/\[\]@\!\$'\(\)\*\+,;=]+)/g,
                            '$1'
                        )
                    );
                });
                return tr;
            }

		} );
	})
}

// Make all the DataTables (each tab)
var constructDataTables = function() {
	var first_table_name = tables[0]

	var $ul = $('
').addClass('nav nav-tabs');
	$.each(tables, function(i, table_name){
		$('
').append('' + table_name + '').bind('click', function(e){
			e.preventDefault();
			$(this).addClass('active').siblings('.active').removeClass('active');
			constructDataTable(i, table_name)
		}).appendTo($ul);
	});
	$ul.appendTo('body');

	constructDataTable(0, first_table_name)
}

// Main entry point, make the data table
var settings
var sqliteEndpoint
var tables
$(function(){
	settings = scraperwiki.readSettings()
	sqliteEndpoint = settings.target.url + '/sqlite'

	scraperwiki.sql("select name from \"sqlite_master\" where type = 'table'", function(data, textStatus, jqXHR) {
		tables = []
		$.each(data, function (i) {
			tables.push(data[i].name)
		})
		console.log("Tables are:", tables)
		constructDataTables()
	}, function(jqXHR, textStatus, errorThrown) {
		showAlert(errorThrown, jqXHR.responseText, "error")
	})

});

<commit_msg>refactoring: use escapeSQL() everywhere<commit_after>// datatables-tool.js

// When there are errors call this
var showAlert = function(title, message, level) {
	var $div;
	level = level || 0;
	$div = $("
").addClass("alert").text(message);
	$div.prepend('<button type="button" class="close" data-dismiss="alert">×</button>');
	$div.prepend("" + title + " ");
	if (level) {
		$div.addClass("alert-error");
	}
	return $div.prependTo("body");
};

var escapeSQL = function(column_name) {
	return "\"" + column_name + "\""
}

// Function to map JSON data between DataTables format and ScraperWiki's SQL endpoint format.
// It returns a function for the fnServerData parameter
var convertData = function(table_name, column_names) {
	// This is a wrapper round the GET request DataTables makes to get more data
	// sSource - the URL, we don't use it, we hard code it instead
	// aoData - contains the URL parameters, e.g. what page, what to filter, what order and so on
	// fnCallback - where to call with the data you get back
	// oSettings - settings object for the whole DataTables, see http://datatables.net/docs/DataTables/1.9.0/DataTable.models.oSettings.html
	return function ( sSource, aoData, fnCallback, oSettings ) {
		// convert aoData into a normal hash (called ps)
		var params = {}
		for (var i=0;i<aoData.length;i++) {
			params[aoData[i].name] = aoData[i].value
		}
		console.log("DataTables params:", params)

		// construct SQL query needed according to the parameters
		var columns  = _.map(column_names, escapeSQL).join(",")
		var order_by = ""
		if (params.iSortingCols >= 1) {
			var order_parts = []
			for (var i = 0; i < params.iSortingCols; i++) {
				order_part = escapeSQL(column_names[params["iSortCol_" + i]])
				if (params["sSortDir_" + i] == 'desc') {
					order_part += " desc"
				} else if (params["sSortDir_" + i] != 'asc') {
					showAlert("Got unknown sSortDir_" + i + " value in table " + table_name)
				}
				order_parts.push(order_part)
			}
			order_by = " order by " + order_parts.join(",")
		}
		var where = ""
		if (params.sSearch) {
			// XXX no idea if this bog standard Javascript escape really does what we want with SQL databases.
			// There's no security risk (as endpoint is sandboxed). There could be user experience pain though.
			var search = "'%" + escape(params.sSearch.toLowerCase()) + "%'"
			where = " where " + _.map(column_names, function(n) { return "lower(" + escapeSQL(n) + ") like " + search }).join(" or ")
		}
		var query = "select " + columns +
			     " from " + escapeSQL(table_name) +
				 where +
				 order_by +
			     " limit " + params.iDisplayLength +
			     " offset " + params.iDisplayStart
		console.log("SQL query:", query)

		// get column counts
		scraperwiki.sql("select (select count(*) from " + escapeSQL(table_name) + ") as total, (select count(*) from " + escapeSQL(table_name) + where + ") as display_total", function (data) {
			var counts = data[0]

			oSettings.jqXHR = $.ajax( {
				"dataType": 'json',
				"type": "GET",
				"url": sqliteEndpoint,
				"data": { q: query },
				"success": function ( response ) {
					// ScraperWiki returns a list of dicts. This converts it to a list of lists.
					var rows = []
					for (var i=0;i<response.length;i++) {
						var row = []
						for (k in response[i]) {
							row.push(response[i][k])
						}
						rows.push(row)
					}
					// Send the data to dataTables
					fnCallback({
						"aaData" : rows,
						"iTotalRecords": data[0].total, // without filtering
						"iTotalDisplayRecords": data[0].display_total // after filtering
					})
				},
				"error": function(jqXHR, textStatus, errorThrown) {
					showAlert(errorThrown, jqXHR.responseText, "error")
				}
			} );
		}, function(jqXHR, textStatus, errorThrown) {
			showAlert(errorThrown, jqXHR.responseText, "error")
		})
	}
}

// Find the column names
function getTableColumnNames(table_name, callback){
    scraperwiki.sql("select * from " + escapeSQL(table_name) + " limit 1", function(data) {
		callback(_.keys(data[0]))
	}, function(jqXHR, textStatus, errorThrown) {
		showAlert(errorThrown, jqXHR.responseText, "error")
	})
}

// Make one of the DataTables (in one tab)
var constructDataTable = function(i, table_name) {
	// Find or make the table
	$(".maintable").hide()
	var id = "table_" + i
	var $outer = $("#" + id)
	if ($outer.length == 0) {
		console.log("making a new table:", table_name)
		$outer = $('

')
		$('body').append($outer)
	} else {
		$outer.show()
		console.log("reusing cached table:", table_name)
		return
	}
	var $t = $outer.find("table")

	// Find out the column names
	getTableColumnNames(table_name, function(column_names) {
		console.log("Columns", column_names)
		if (column_names.length == 0) {
			showAlert("No data in the table", jqXHR.responseText)
			return
		}

		// Make the column headings
        var thead = '

'
		_.each(column_names, function(column_name) {
			thead += '' + column_name + '	'
		})
		thead += '

'
		$t.append(thead)

		// Fill in the datatables object
		$t.dataTable( {
			"bProcessing": true,
			"bServerSide": true,
			"bDeferRender": true,
			"bPaginate": true,
			"sPaginationType": "full_numbers",
			"bFilter": true,
			"iDisplayLength": 100,
            "bScrollCollapse": true,
//            "sDom": '<"H"<"#schema_'+table_name+'">lfr>t<"F"ip>',
            "sDom": '<"H"fip>rt<"F"pil>',
			"aLengthMenu": [10, 100, 1000],
			"fnServerData": convertData(table_name, column_names),
            "fnRowCallback": function( tr, array, iDisplayIndex, iDisplayIndexFull ) {
                $('td', tr).each(function(){
                    $(this).html(
                        $(this).html().replace(
                            /((http|https|ftp):\/\/[a-zA-Z0-9-_~#:\.\?%&\/\[\]@\!\$'\(\)\*\+,;=]+)/g,
                            '$1'
                        )
                    );
                });
                return tr;
            }

		} );
	})
}

// Make all the DataTables (each tab)
var constructDataTables = function() {
	var first_table_name = tables[0]

	var $ul = $('
').addClass('nav nav-tabs');
	$.each(tables, function(i, table_name){
		$('
').append('' + table_name + '').bind('click', function(e){
			e.preventDefault();
			$(this).addClass('active').siblings('.active').removeClass('active');
			constructDataTable(i, table_name)
		}).appendTo($ul);
	});
	$ul.appendTo('body');

	constructDataTable(0, first_table_name)
}

// Main entry point, make the data table
var settings
var sqliteEndpoint
var tables
$(function(){
	settings = scraperwiki.readSettings()
	sqliteEndpoint = settings.target.url + '/sqlite'

	scraperwiki.sql("select name from " + escapeSQL('sqlite_master') + " where type = 'table'", function(data, textStatus, jqXHR) {
		tables = []
		$.each(data, function (i) {
			tables.push(data[i].name)
		})
		console.log("Tables are:", tables)
		constructDataTables()
	}, function(jqXHR, textStatus, errorThrown) {
		showAlert(errorThrown, jqXHR.responseText, "error")
	})

});

<|endoftext|>


<commit_before>import window from 'global';
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import memoize from 'memoizerific';
import copy from 'copy-to-clipboard';
import { styled } from '@storybook/theming';
import { SET_CURRENT_STORY } from '@storybook/core-events';
import addons, { types } from '@storybook/addons';
import merge from '@storybook/api/dist/lib/merge';
import { Icons, IconButton, TabButton, TabBar, Separator } from '@storybook/components';

import { Helmet } from 'react-helmet-async';

import { Toolbar } from './toolbar';

import * as S from './components';

import { ZoomProvider, ZoomConsumer, Zoom } from './zoom';

import { IFrame } from './iframe';

const DesktopOnly = styled.span({
  // Hides full screen icon at mobile breakpoint defined in app.js
  '@media (max-width: 599px)': {
    display: 'none',
  },
});
const stringifyQueryParams = queryParams =>
  Object.entries(queryParams).reduce((acc, [k, v]) => {
    return `${acc}&${k}=${v}`;
  }, '');

const renderIframe = (storyId, viewMode, id, baseUrl, scale, queryParams) => (
  <IFrame
    key="iframe"
    id="storybook-preview-iframe"
    title={id || 'preview'}
    src={`${baseUrl}?id=${storyId}&viewMode=${viewMode}${stringifyQueryParams(queryParams)}`}
    allowFullScreen
    scale={scale}
  />
);

const getElementList = memoize(10)((getFn, type, base) => base.concat(Object.values(getFn(type))));

const ActualPreview = ({
  wrappers,
  viewMode,
  id,
  storyId,
  active,
  baseUrl,
  scale,
  queryParams,
  customCanvas,
}) => {
  const data = [storyId, viewMode, id, baseUrl, scale, queryParams];
  const base = customCanvas ? customCanvas(...data) : renderIframe(...data);
  return wrappers.reduceRight(
    (acc, wrapper, index) => wrapper.render({ index, children: acc, id, storyId, active }),
    base
  );
};

const IframeWrapper = styled.div(({ theme }) => ({
  position: 'absolute',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0,
  width: '100%',
  height: '100%',
  background: theme.background.content,
}));

const defaultWrappers = [
  {
    render: p => (
      <IframeWrapper id="storybook-preview-wrapper" hidden={!p.active}>
        {p.children}
      </IframeWrapper>
    ),
  },
];

const getTools = memoize(10)(
  (getElements, queryParams, panels, api, options, storyId, viewMode, location, path, baseUrl) => {
    const tools = getElementList(getElements, types.TOOL, [
      panels.filter(p => p.id !== 'canvas').length
        ? {
            render: () => (
              <Fragment>
                <TabBar key="tabs" scroll={false}>
                  {panels.map((t, index) => {
                    const to = t.route({ storyId, viewMode, path, location });
                    const isActive = path === to;
                    return (
                      <S.UnstyledLink key={t.id || `l${index}`} to={to}>
                        <TabButton disabled={t.disabled} active={isActive}>
                          {t.title}
                        </TabButton>
                      </S.UnstyledLink>
                    );
                  })}
                </TabBar>
                <Separator />
              </Fragment>
            ),
          }
        : null,
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <Fragment>
            <ZoomConsumer>
              {({ set, value }) => (
                <Zoom key="zoom" current={value} set={v => set(value * v)} reset={() => set(1)} />
              )}
            </ZoomConsumer>
            <Separator />
          </Fragment>
        ),
      },
    ]);

    const extraTools = getElementList(getElements, types.TOOLEXTRA, [
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <DesktopOnly>
            <IconButton
              key="full"
              onClick={api.toggleFullscreen}
              title={options.isFullscreen ? 'Exit full screen' : 'Go full screen'}
            >
              <Icons icon={options.isFullscreen ? 'close' : 'expand'} />
            </IconButton>
          </DesktopOnly>
        ),
      },
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <IconButton
            key="opener"
            onClick={() =>
              window.open(`${baseUrl}?id=${storyId}${stringifyQueryParams(queryParams)}`)
            }
            title="Open canvas in new tab"
          >
            <Icons icon="share" />
          </IconButton>
        ),
      },
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <IconButton
            key="copy"
            onClick={() =>
              copy(
                `${window.location.origin}${
                  window.location.pathname
                }${baseUrl}?id=${storyId}${stringifyQueryParams(queryParams)}`
              )
            }
            title="Copy canvas link"
          >
            <Icons icon="copy" />
          </IconButton>
        ),
      },
    ]);

    const filter = item =>
      item && (!item.match || item.match({ storyId, viewMode, location, path }));

    const displayItems = list =>
      list.reduce(
        (acc, item, index) =>
          item ? (
            <Fragment key={item.id || item.key || `f-${index}`}>
              {acc}
              {item.render() || item}
            </Fragment>
          ) : (
            acc
          ),
        null
      );

    const left = displayItems(tools.filter(filter));
    const right = displayItems(extraTools.filter(filter));

    return { left, right };
  }
);

const getDocumentTitle = description => {
  return description ? `${description} ⋅ Storybook` : 'Storybook';
};

class Preview extends Component {
  shouldComponentUpdate({ storyId, viewMode, options, queryParams, parameters }) {
    const { props } = this;

    return (
      options.isFullscreen !== props.options.isFullscreen ||
      options.isToolshown !== props.options.isToolshown ||
      viewMode !== props.viewMode ||
      storyId !== props.storyId ||
      queryParams !== props.queryParams ||
      parameters !== props.parameters
    );
  }

  componentDidUpdate(prevProps) {
    const { api, storyId, viewMode } = this.props;
    const { storyId: prevStoryId, viewMode: prevViewMode } = prevProps;
    if ((storyId && storyId !== prevStoryId) || (viewMode && viewMode !== prevViewMode)) {
      api.emit(SET_CURRENT_STORY, { storyId, viewMode });
    }
  }

  render() {
    const {
      id,
      path,
      location,
      viewMode,
      storyId,
      queryParams,
      getElements,
      api,
      customCanvas,
      options,
      description,
      baseUrl,
      parameters,
    } = this.props;
    const toolbarHeight = options.isToolshown ? 40 : 0;
    const wrappers = getElementList(getElements, types.PREVIEW, defaultWrappers);
    let panels = getElementList(getElements, types.TAB, [
      {
        route: p => `/story/${p.storyId}`,
        match: p => p.viewMode && p.viewMode.match(/^(story|docs)$/),
        render: p => (
          <ZoomConsumer>
            {({ value }) => {
              const props = {
                viewMode,
                active: p.active,
                wrappers,
                id,
                storyId,
                baseUrl,
                queryParams,
                scale: value,
                customCanvas,
              };

              return <ActualPreview {...props} />;
            }}
          </ZoomConsumer>
        ),
        title: 'Canvas',
        id: 'canvas',
      },
    ]);
    const { previewTabs } = addons.getConfig();
    const parametersTabs = parameters ? parameters.previewTabs : undefined;
    if (previewTabs || parametersTabs) {
      // deep merge global and local settings
      const tabs = merge(previewTabs, parametersTabs);
      const arrTabs = Object.keys(tabs).map((key, index) => ({
        index,
        ...(typeof tabs[key] === 'string' ? { title: tabs[key] } : tabs[key]),
        id: key,
      }));
      panels = panels
        .filter(panel => {
          const t = arrTabs.find(tab => tab.id === panel.id);
          return t === undefined || !t.hidden;
        })
        .map((panel, index) => ({ ...panel, index }))
        .sort((p1, p2) => {
          const tab_1 = arrTabs.find(tab => tab.id === p1.id);
          const index_1 = tab_1 ? tab_1.index : arrTabs.length + p1.index;
          const tab_2 = arrTabs.find(tab => tab.id === p2.id);
          const index_2 = tab_2 ? tab_2.index : arrTabs.length + p2.index;
          return index_1 - index_2;
        })
        .map(panel => {
          const t = arrTabs.find(tab => tab.id === panel.id);
          if (t) {
            return { ...panel, title: t.title || panel.title, disabled: t.disabled };
          }
          return panel;
        });
    }
    const { left, right } = getTools(
      getElements,
      queryParams,
      panels,
      api,
      options,
      storyId,
      viewMode,
      location,
      path,
      baseUrl
    );

    return (
      <ZoomProvider>
        <Fragment>
          {id === 'main' && (
            <Helmet key="description">
              <title>{getDocumentTitle(description)}</title>
            </Helmet>
          )}
          <Toolbar key="toolbar" shown={options.isToolshown} border>
            <Fragment key="left">{left}</Fragment>
            <Fragment key="right">{right}</Fragment>
          </Toolbar>
          <S.FrameWrap key="frame" offset={toolbarHeight}>
            {panels.map(p => (
              <Fragment key={p.id || p.key}>
                {p.render({ active: p.match({ storyId, viewMode, location, path }) })}
              </Fragment>
            ))}
          </S.FrameWrap>
        </Fragment>
      </ZoomProvider>
    );
  }
}
Preview.propTypes = {
  id: PropTypes.string.isRequired,
  description: PropTypes.string,
  customCanvas: PropTypes.func,
  api: PropTypes.shape({
    on: PropTypes.func,
    off: PropTypes.func,
    emit: PropTypes.func,
    toggleFullscreen: PropTypes.func,
  }).isRequired,
  storyId: PropTypes.string,
  path: PropTypes.string,
  viewMode: PropTypes.string,
  location: PropTypes.shape({}).isRequired,
  getElements: PropTypes.func.isRequired,
  queryParams: PropTypes.shape({}).isRequired,
  options: PropTypes.shape({
    isFullscreen: PropTypes.bool,
    isToolshown: PropTypes.bool,
  }).isRequired,
  baseUrl: PropTypes.string,
  parameters: PropTypes.shape({
    previewTabs: PropTypes.shape({
      title: PropTypes.string,
      hidden: PropTypes.string,
      disabled: PropTypes.string,
    }),
  }),
};

Preview.defaultProps = {
  viewMode: undefined,
  storyId: undefined,
  path: undefined,
  description: undefined,
  baseUrl: 'iframe.html',
  customCanvas: undefined,
  parameters: undefined,
};

export { Preview };
<commit_msg>keep story panel even hidden or causes issues<commit_after>import window from 'global';
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import memoize from 'memoizerific';
import copy from 'copy-to-clipboard';
import { styled } from '@storybook/theming';
import { SET_CURRENT_STORY } from '@storybook/core-events';
import addons, { types } from '@storybook/addons';
import merge from '@storybook/api/dist/lib/merge';
import { Icons, IconButton, TabButton, TabBar, Separator } from '@storybook/components';

import { Helmet } from 'react-helmet-async';

import { Toolbar } from './toolbar';

import * as S from './components';

import { ZoomProvider, ZoomConsumer, Zoom } from './zoom';

import { IFrame } from './iframe';

const DesktopOnly = styled.span({
  // Hides full screen icon at mobile breakpoint defined in app.js
  '@media (max-width: 599px)': {
    display: 'none',
  },
});
const stringifyQueryParams = queryParams =>
  Object.entries(queryParams).reduce((acc, [k, v]) => {
    return `${acc}&${k}=${v}`;
  }, '');

const renderIframe = (storyId, viewMode, id, baseUrl, scale, queryParams) => (
  <IFrame
    key="iframe"
    id="storybook-preview-iframe"
    title={id || 'preview'}
    src={`${baseUrl}?id=${storyId}&viewMode=${viewMode}${stringifyQueryParams(queryParams)}`}
    allowFullScreen
    scale={scale}
  />
);

const getElementList = memoize(10)((getFn, type, base) => base.concat(Object.values(getFn(type))));

const ActualPreview = ({
  wrappers,
  viewMode,
  id,
  storyId,
  active,
  baseUrl,
  scale,
  queryParams,
  customCanvas,
}) => {
  const data = [storyId, viewMode, id, baseUrl, scale, queryParams];
  const base = customCanvas ? customCanvas(...data) : renderIframe(...data);
  return wrappers.reduceRight(
    (acc, wrapper, index) => wrapper.render({ index, children: acc, id, storyId, active }),
    base
  );
};

const IframeWrapper = styled.div(({ theme }) => ({
  position: 'absolute',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0,
  width: '100%',
  height: '100%',
  background: theme.background.content,
}));

const defaultWrappers = [
  {
    render: p => (
      <IframeWrapper id="storybook-preview-wrapper" hidden={!p.active}>
        {p.children}
      </IframeWrapper>
    ),
  },
];

const getTools = memoize(10)(
  (getElements, queryParams, panels, api, options, storyId, viewMode, location, path, baseUrl) => {
    const tools = getElementList(getElements, types.TOOL, [
      panels.filter(p => !p.hidden).length > 1
        ? {
            render: () => (
              <Fragment>
                <TabBar key="tabs" scroll={false}>
                  {panels
                    .filter(p => !p.hidden)
                    .map((t, index) => {
                      const to = t.route({ storyId, viewMode, path, location });
                      const isActive = path === to;
                      return (
                        <S.UnstyledLink key={t.id || `l${index}`} to={to}>
                          <TabButton disabled={t.disabled} active={isActive}>
                            {t.title}
                          </TabButton>
                        </S.UnstyledLink>
                      );
                    })}
                </TabBar>
                <Separator />
              </Fragment>
            ),
          }
        : null,
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <Fragment>
            <ZoomConsumer>
              {({ set, value }) => (
                <Zoom key="zoom" current={value} set={v => set(value * v)} reset={() => set(1)} />
              )}
            </ZoomConsumer>
            <Separator />
          </Fragment>
        ),
      },
    ]);

    const extraTools = getElementList(getElements, types.TOOLEXTRA, [
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <DesktopOnly>
            <IconButton
              key="full"
              onClick={api.toggleFullscreen}
              title={options.isFullscreen ? 'Exit full screen' : 'Go full screen'}
            >
              <Icons icon={options.isFullscreen ? 'close' : 'expand'} />
            </IconButton>
          </DesktopOnly>
        ),
      },
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <IconButton
            key="opener"
            onClick={() =>
              window.open(`${baseUrl}?id=${storyId}${stringifyQueryParams(queryParams)}`)
            }
            title="Open canvas in new tab"
          >
            <Icons icon="share" />
          </IconButton>
        ),
      },
      {
        match: p => p.viewMode === 'story',
        render: () => (
          <IconButton
            key="copy"
            onClick={() =>
              copy(
                `${window.location.origin}${
                  window.location.pathname
                }${baseUrl}?id=${storyId}${stringifyQueryParams(queryParams)}`
              )
            }
            title="Copy canvas link"
          >
            <Icons icon="copy" />
          </IconButton>
        ),
      },
    ]);

    const filter = item =>
      item && (!item.match || item.match({ storyId, viewMode, location, path }));

    const displayItems = list =>
      list.reduce(
        (acc, item, index) =>
          item ? (
            <Fragment key={item.id || item.key || `f-${index}`}>
              {acc}
              {item.render() || item}
            </Fragment>
          ) : (
            acc
          ),
        null
      );

    const left = displayItems(tools.filter(filter));
    const right = displayItems(extraTools.filter(filter));

    return { left, right };
  }
);

const getDocumentTitle = description => {
  return description ? `${description} ⋅ Storybook` : 'Storybook';
};

class Preview extends Component {
  shouldComponentUpdate({ storyId, viewMode, options, queryParams, parameters }) {
    const { props } = this;

    return (
      options.isFullscreen !== props.options.isFullscreen ||
      options.isToolshown !== props.options.isToolshown ||
      viewMode !== props.viewMode ||
      storyId !== props.storyId ||
      queryParams !== props.queryParams ||
      parameters !== props.parameters
    );
  }

  componentDidUpdate(prevProps) {
    const { api, storyId, viewMode } = this.props;
    const { storyId: prevStoryId, viewMode: prevViewMode } = prevProps;
    if ((storyId && storyId !== prevStoryId) || (viewMode && viewMode !== prevViewMode)) {
      api.emit(SET_CURRENT_STORY, { storyId, viewMode });
    }
  }

  render() {
    const {
      id,
      path,
      location,
      viewMode,
      storyId,
      queryParams,
      getElements,
      api,
      customCanvas,
      options,
      description,
      baseUrl,
      parameters,
    } = this.props;
    const toolbarHeight = options.isToolshown ? 40 : 0;
    const wrappers = getElementList(getElements, types.PREVIEW, defaultWrappers);
    let panels = getElementList(getElements, types.TAB, [
      {
        route: p => `/story/${p.storyId}`,
        match: p => p.viewMode && p.viewMode.match(/^(story|docs)$/),
        render: p => (
          <ZoomConsumer>
            {({ value }) => {
              const props = {
                viewMode,
                active: p.active,
                wrappers,
                id,
                storyId,
                baseUrl,
                queryParams,
                scale: value,
                customCanvas,
              };

              return <ActualPreview {...props} />;
            }}
          </ZoomConsumer>
        ),
        title: 'Canvas',
        id: 'canvas',
      },
    ]);
    const { previewTabs } = addons.getConfig();
    const parametersTabs = parameters ? parameters.previewTabs : undefined;
    if (previewTabs || parametersTabs) {
      // deep merge global and local settings
      const tabs = merge(previewTabs, parametersTabs);
      const arrTabs = Object.keys(tabs).map((key, index) => ({
        index,
        ...(typeof tabs[key] === 'string' ? { title: tabs[key] } : tabs[key]),
        id: key,
      }));
      panels = panels
        .filter(panel => {
          const t = arrTabs.find(tab => tab.id === panel.id);
          return t === undefined || t.id === 'canvas' || !t.hidden;
        })
        .map((panel, index) => ({ ...panel, index }))
        .sort((p1, p2) => {
          const tab_1 = arrTabs.find(tab => tab.id === p1.id);
          const index_1 = tab_1 ? tab_1.index : arrTabs.length + p1.index;
          const tab_2 = arrTabs.find(tab => tab.id === p2.id);
          const index_2 = tab_2 ? tab_2.index : arrTabs.length + p2.index;
          return index_1 - index_2;
        })
        .map(panel => {
          const t = arrTabs.find(tab => tab.id === panel.id);
          if (t) {
            return {
              ...panel,
              title: t.title || panel.title,
              disabled: t.disabled,
              hidden: t.hidden,
            };
          }
          return panel;
        });
    }
    const { left, right } = getTools(
      getElements,
      queryParams,
      panels,
      api,
      options,
      storyId,
      viewMode,
      location,
      path,
      baseUrl
    );

    return (
      <ZoomProvider>
        <Fragment>
          {id === 'main' && (
            <Helmet key="description">
              <title>{getDocumentTitle(description)}</title>
            </Helmet>
          )}
          <Toolbar key="toolbar" shown={options.isToolshown} border>
            <Fragment key="left">{left}</Fragment>
            <Fragment key="right">{right}</Fragment>
          </Toolbar>
          <S.FrameWrap key="frame" offset={toolbarHeight}>
            {panels.map(p => (
              <Fragment key={p.id || p.key}>
                {p.render({ active: p.match({ storyId, viewMode, location, path }) })}
              </Fragment>
            ))}
          </S.FrameWrap>
        </Fragment>
      </ZoomProvider>
    );
  }
}
Preview.propTypes = {
  id: PropTypes.string.isRequired,
  description: PropTypes.string,
  customCanvas: PropTypes.func,
  api: PropTypes.shape({
    on: PropTypes.func,
    off: PropTypes.func,
    emit: PropTypes.func,
    toggleFullscreen: PropTypes.func,
  }).isRequired,
  storyId: PropTypes.string,
  path: PropTypes.string,
  viewMode: PropTypes.string,
  location: PropTypes.shape({}).isRequired,
  getElements: PropTypes.func.isRequired,
  queryParams: PropTypes.shape({}).isRequired,
  options: PropTypes.shape({
    isFullscreen: PropTypes.bool,
    isToolshown: PropTypes.bool,
  }).isRequired,
  baseUrl: PropTypes.string,
  parameters: PropTypes.shape({
    previewTabs: PropTypes.shape({
      title: PropTypes.string,
      hidden: PropTypes.string,
      disabled: PropTypes.string,
    }),
  }),
};

Preview.defaultProps = {
  viewMode: undefined,
  storyId: undefined,
  path: undefined,
  description: undefined,
  baseUrl: 'iframe.html',
  customCanvas: undefined,
  parameters: undefined,
};

export { Preview };
<|endoftext|>


<commit_before>// Convert an element into a 3D laptop, moving its contents into the screen.

class Laptop {
  constructor(elem, index, dummy = false) {
    this.elem = elem;
    this.index = index;
    this.dummy = dummy;

    this._translateX = this._translateY = this._translateZ = 0;
    this._rotateX = '80deg'; this._rotateY = 0; this._rotateZ = '45deg';

    this.elem.classList.add('laptop3d');
    if (this.dummy) this.elem.classList.add('dummy');
    if (typeof this.index !== 'undefined') this.elem.setAttribute('data-laptop3d-index', this.index);

    const children = [...this.elem.childNodes];
    this.elem.innerHTML = `

    `;
    // Record some common elements as properties
    ['base', 'lid', 'screen'].forEach((name) => {
      this[name] = this.elem.getElementsByClassName(name)[0];
    });
    // Add all former children to the new screen element
    children.forEach(child => this.screen.appendChild(child));

    // Transform
    this._translateX = `${30 * this.index}vw`;

    // Lift up a bit on hover
    if (!this.dummy) {
      this.elem.addEventListener('mouseenter', () => { this.translateZ = '1vw'; });
      this.elem.addEventListener('mouseleave', () => { this.translateZ = 0; });
    }

    this._applyTransform();
  }

  // Allow getting and setting transformation properties individually
  _applyTransform() {
    this.elem.style.transform = [
      'translateX(50vw) translateX(-50%)',
      `rotateX(${this._rotateX})`,
      `rotateY(${this._rotateY})`,
      `rotateZ(${this._rotateZ})`,
      `translateX(${this._translateX})`,
      `translateY(${this._translateY})`,
      `translateZ(${this._translateZ})`,
    ].join(' ');
  }

  get translateX() { return this._translateX; }
  get translateY() { return this._translateY; }
  get translateZ() { return this._translateZ; }
  get rotateX() { return this._rotateX; }
  get rotateY() { return this._rotateY; }
  get rotateZ() { return this._rotateZ; }

  set translateX(val) { this._translateX = val; this._applyTransform(); }
  set translateY(val) { this._translateY = val; this._applyTransform(); }
  set translateZ(val) { this._translateZ = val; this._applyTransform(); }

  _setRotate(axis, val) {
    if (!['X', 'Y', 'Z'].includes(axis)) throw new Error('Axis must be one of X Y or Z'); // val must be XYZ
    const prop = `_rotate${axis}`; // Property that we're setting
    if (typeof val === 'string' && val.endsWith('deg')) this[prop] = val; // Don't change if the value is already a string ending in 'deg'
    else this[prop] = `${val}deg`; // Otherwise add the 'deg' suffix
    this._applyTransform();
  }
  set rotateX(val) { this._setRotate('X', val); }
  set rotateY(val) { this._setRotate('Y', val); }
  set rotateZ(val) { this._setRotate('Z', val); }
}

class LaptopCarousel {
  constructor(container, laptops) {
    this.container = container;
    this.laptops = laptops;
    // Sort by index
    this.laptops.sort((a, b) => a.index - b.index);
    // Check that there are no overlapping indices
    const indices = this.laptops.map(l => l.index);
    if (indices.length !== new Set(indices).size) throw new Error('LaptopCarousel indices must be unique');
    // Record this as the carousel for each laptop
    laptops.forEach((l) => { l.carousel = this; });
    // Build map of laptops by index
    this.laptopsByIndex = {};
    laptops.forEach((l) => { this.laptopsByIndex[l.index] = l; });
  }

  scroll(progress) {
    this.progress = progress;
  }
}

// Set up laptop scene
document.addEventListener('DOMContentLoaded', () => {
  // Convert all elements with the class 'laptop3d' into laptops
  const laptopsContainer = document.getElementsByClassName('laptops-container')[0];
  const laptopElems = [...laptopsContainer.getElementsByClassName('laptop3d')];
  const laptops = laptopElems.map((l, i) => new Laptop(l, i));
  // Add 10 dummy laptops to the beginning
  const dummyLaptops = new Array(10)
    .fill(0)
    .map(() => document.createElement('div'))
    .map(l => laptopsContainer.appendChild(l))
    .map((l, i) => new Laptop(l, -(i + 1), true));

  window.carousel = new LaptopCarousel(laptopsContainer, laptops.concat(dummyLaptops));
});
<commit_msg>Easily get max and min index of laptops in carousel<commit_after>// Convert an element into a 3D laptop, moving its contents into the screen.

class Laptop {
  constructor(elem, index, dummy = false) {
    this.elem = elem;
    this.index = index;
    this.dummy = dummy;

    this._translateX = this._translateY = this._translateZ = 0;
    this._rotateX = '80deg'; this._rotateY = 0; this._rotateZ = '45deg';

    this.elem.classList.add('laptop3d');
    if (this.dummy) this.elem.classList.add('dummy');
    if (typeof this.index !== 'undefined') this.elem.setAttribute('data-laptop3d-index', this.index);

    const children = [...this.elem.childNodes];
    this.elem.innerHTML = `

    `;
    // Record some common elements as properties
    ['base', 'lid', 'screen'].forEach((name) => {
      this[name] = this.elem.getElementsByClassName(name)[0];
    });
    // Add all former children to the new screen element
    children.forEach(child => this.screen.appendChild(child));

    // Transform
    this._translateX = `${30 * this.index}vw`;

    // Lift up a bit on hover
    if (!this.dummy) {
      this.elem.addEventListener('mouseenter', () => { this.translateZ = '1vw'; });
      this.elem.addEventListener('mouseleave', () => { this.translateZ = 0; });
    }

    this._applyTransform();
  }

  // Allow getting and setting transformation properties individually
  _applyTransform() {
    this.elem.style.transform = [
      'translateX(50vw) translateX(-50%)',
      `rotateX(${this._rotateX})`,
      `rotateY(${this._rotateY})`,
      `rotateZ(${this._rotateZ})`,
      `translateX(${this._translateX})`,
      `translateY(${this._translateY})`,
      `translateZ(${this._translateZ})`,
    ].join(' ');
  }

  get translateX() { return this._translateX; }
  get translateY() { return this._translateY; }
  get translateZ() { return this._translateZ; }
  get rotateX() { return this._rotateX; }
  get rotateY() { return this._rotateY; }
  get rotateZ() { return this._rotateZ; }

  set translateX(val) { this._translateX = val; this._applyTransform(); }
  set translateY(val) { this._translateY = val; this._applyTransform(); }
  set translateZ(val) { this._translateZ = val; this._applyTransform(); }

  _setRotate(axis, val) {
    if (!['X', 'Y', 'Z'].includes(axis)) throw new Error('Axis must be one of X Y or Z'); // val must be XYZ
    const prop = `_rotate${axis}`; // Property that we're setting
    if (typeof val === 'string' && val.endsWith('deg')) this[prop] = val; // Don't change if the value is already a string ending in 'deg'
    else this[prop] = `${val}deg`; // Otherwise add the 'deg' suffix
    this._applyTransform();
  }
  set rotateX(val) { this._setRotate('X', val); }
  set rotateY(val) { this._setRotate('Y', val); }
  set rotateZ(val) { this._setRotate('Z', val); }
}

class LaptopCarousel {
  constructor(container, laptops) {
    this.container = container;
    this.laptops = laptops;
    // Sort by index
    this.laptops.sort((a, b) => a.index - b.index);
    // Check that there are no overlapping indices
    const indices = this.laptops.map(l => l.index);
    if (indices.length !== new Set(indices).size) throw new Error('LaptopCarousel indices must be unique');
    // Record this as the carousel for each laptop
    laptops.forEach((l) => { l.carousel = this; });
    // Build map of laptops by index
    this.laptopsByIndex = {};
    laptops.forEach((l) => { this.laptopsByIndex[l.index] = l; });
  }

  // Minimum index represented
  get minIndex() {
    return Math.min(...this.laptops.map(l => l.index));
  }

  // Maximum index represented
  get maxIndex() {
    return Math.max(...this.laptops.map(l => l.index));
  }

  scroll(progress) {
    this.progress = progress;
  }
}

// Set up laptop scene
document.addEventListener('DOMContentLoaded', () => {
  // Convert all elements with the class 'laptop3d' into laptops
  const laptopsContainer = document.getElementsByClassName('laptops-container')[0];
  const laptopElems = [...laptopsContainer.getElementsByClassName('laptop3d')];
  const laptops = laptopElems.map((l, i) => new Laptop(l, i));
  // Add 10 dummy laptops to the beginning
  const dummyLaptops = new Array(10)
    .fill(0)
    .map(() => document.createElement('div'))
    .map(l => laptopsContainer.appendChild(l))
    .map((l, i) => new Laptop(l, -(i + 1), true));

  window.carousel = new LaptopCarousel(laptopsContainer, laptops.concat(dummyLaptops));
});
<|endoftext|>


<commit_before>var list = {

	"Manual": {
		"Introduction": [
			[ "Creating a scene", "manual/introduction/Creating-a-scene" ],
			[ "Matrix transformations", "manual/introduction/Matrix-transformations" ]
		]
	},

	"Reference": {
		"Constants": [
			[ "CustomBlendingEquation", "api/constants/CustomBlendingEquations"],
			[ "GLState", "api/constants/GLState"],
			[ "Materials", "api/constants/Materials"],
			[ "ShadowingTypes", "api/constants/ShadowingTypes"],
			[ "Textures", "api/constants/Textures"]
		],

		"Cameras": [
			[ "Camera", "api/cameras/Camera" ],
			[ "CubeCamera", "api/cameras/CubeCamera" ],
			[ "OrthographicCamera", "api/cameras/OrthographicCamera" ],
			[ "PerspectiveCamera", "api/cameras/PerspectiveCamera" ]
		],

		"Core": [
			[ "BufferAttribute", "api/core/BufferAttribute" ],
			[ "BufferGeometry", "api/core/BufferGeometry" ],
			[ "Clock", "api/core/Clock" ],
			[ "EventDispatcher", "api/core/EventDispatcher" ],
			[ "Face3", "api/core/Face3" ],
			[ "Geometry", "api/core/Geometry" ],
			[ "Object3D", "api/core/Object3D" ],
			[ "Raycaster", "api/core/Raycaster" ]
		],

		"Lights": [
			[ "AmbientLight", "api/lights/AmbientLight" ],
			[ "DirectionalLight", "api/lights/DirectionalLight" ],
			[ "HemisphereLight", "api/lights/HemisphereLight" ],
			[ "Light", "api/lights/Light" ],
			[ "PointLight", "api/lights/PointLight" ],
			[ "SpotLight", "api/lights/SpotLight" ]
		],

		"Loaders": [
			[ "BabylonLoader", "api/loaders/BabylonLoader" ],
			[ "BufferGeometryLoader", "api/loaders/BufferGeometryLoader" ],
			[ "Cache", "api/loaders/Cache" ],
			[ "ColladaLoader", "api/loaders/ColladaLoader" ],
			[ "glTFLoader", "api/loaders/glTFLoader" ],
			[ "ImageLoader", "api/loaders/ImageLoader" ],
			[ "JSONLoader", "api/loaders/JSONLoader" ],
			[ "Loader", "api/loaders/Loader" ],
			[ "LoadingManager", "api/loaders/LoadingManager" ],
			[ "MaterialLoader", "api/loaders/MaterialLoader" ],
			[ "MTLLoader", "api/loaders/MTLLoader" ],
			[ "OBJLoader", "api/loaders/OBJLoader" ],
			[ "OBJMTLLoader", "api/loaders/OBJMTLLoader" ],
			[ "ObjectLoader", "api/loaders/ObjectLoader" ],
			[ "PDBLoader", "api/loaders/PDBLoader" ],
			[ "SVGLoader", "api/loaders/SVGLoader" ],
			[ "TextureLoader", "api/loaders/TextureLoader" ],
			[ "TGALoader", "api/loaders/TGALoader" ],
			[ "XHRLoader", "api/loaders/XHRLoader" ]
		],

		"Materials": [
			[ "LineBasicMaterial", "api/materials/LineBasicMaterial" ],
			[ "LineDashedMaterial", "api/materials/LineDashedMaterial" ],
			[ "Material", "api/materials/Material" ],
			[ "MeshBasicMaterial", "api/materials/MeshBasicMaterial" ],
			[ "MeshDepthMaterial", "api/materials/MeshDepthMaterial" ],
			[ "MeshFaceMaterial", "api/materials/MeshFaceMaterial" ],
			[ "MeshLambertMaterial", "api/materials/MeshLambertMaterial" ],
			[ "MeshNormalMaterial", "api/materials/MeshNormalMaterial" ],
			[ "MeshPhongMaterial", "api/materials/MeshPhongMaterial" ],
			[ "PointsMaterial", "api/materials/PointsMaterial" ],
			[ "RawShaderMaterial", "api/materials/RawShaderMaterial" ],
			[ "ShaderMaterial", "api/materials/ShaderMaterial" ],
			[ "SpriteCanvasMaterial", "api/materials/SpriteCanvasMaterial" ],
			[ "SpriteMaterial", "api/materials/SpriteMaterial" ]
		],

		"Math": [
			[ "Box2", "api/math/Box2" ],
			[ "Box3", "api/math/Box3" ],
			[ "Color", "api/math/Color" ],
			[ "Euler", "api/math/Euler" ],
			[ "Frustum", "api/math/Frustum" ],
			[ "Line3", "api/math/Line3" ],
			[ "Math", "api/math/Math" ],
			[ "Matrix3", "api/math/Matrix3" ],
			[ "Matrix4", "api/math/Matrix4" ],
			[ "Plane", "api/math/Plane" ],
			[ "Quaternion", "api/math/Quaternion" ],
			[ "Ray", "api/math/Ray" ],
			[ "Sphere", "api/math/Sphere" ],
			[ "Spline", "api/math/Spline" ],
			[ "Triangle", "api/math/Triangle" ],
			[ "Vector2", "api/math/Vector2" ],
			[ "Vector3", "api/math/Vector3" ],
			[ "Vector4", "api/math/Vector4" ]
		],

		"Objects": [
			[ "Bone", "api/objects/Bone" ],
			[ "LensFlare", "api/objects/LensFlare" ],
			[ "Line", "api/objects/Line" ],
			[ "LineSegments", "api/objects/LineSegments" ],
			[ "LOD", "api/objects/LOD" ],
			[ "Mesh", "api/objects/Mesh" ],
			[ "MorphAnimMesh", "api/objects/MorphAnimMesh" ],
			[ "Points", "api/objects/Points" ],
			[ "SkinnedMesh", "api/objects/SkinnedMesh" ],
			[ "Skeleton", "api/objects/Skeleton" ],
			[ "Sprite", "api/objects/Sprite" ]
		],

		"Renderers": [
			[ "CanvasRenderer", "api/renderers/CanvasRenderer" ],
			[ "WebGLRenderer", "api/renderers/WebGLRenderer" ],
			[ "WebGLRenderTarget", "api/renderers/WebGLRenderTarget" ],
			[ "WebGLRenderTargetCube", "api/renderers/WebGLRenderTargetCube" ]
		],

		"Renderers / Shaders": [
			[ "ShaderChunk", "api/renderers/shaders/ShaderChunk" ],
			[ "ShaderLib", "api/renderers/shaders/ShaderLib" ],
			[ "UniformsLib", "api/renderers/shaders/UniformsLib" ],
			[ "UniformsUtils", "api/renderers/shaders/UniformsUtils" ]
		],

		"Renderers / WebGL": [
			[ "WebGLProgram", "api/renderers/webgl/WebGLProgram" ],
			[ "WebGLShader", "api/renderers/webgl/WebGLShader" ],
			[ "WebGLState", "api/renderers/webgl/WebGLState" ]
		],

		"Renderers / WebGL / Plugins": [
			[ "LensFlarePlugin", "api/renderers/webgl/plugins/LensFlarePlugin" ],
			[ "ShadowMapPlugin", "api/renderers/webgl/plugins/ShadowMapPlugin" ],
			[ "SpritePlugin", "api/renderers/webgl/plugins/SpritePlugin" ]
		],

		"Scenes": [
			[ "Fog", "api/scenes/Fog" ],
			[ "FogExp2", "api/scenes/FogExp2" ],
			[ "Scene", "api/scenes/Scene" ]
		],

		"Textures": [
			[ "CubeTexture", "api/textures/CubeTexture" ],
			[ "CompressedTexture", "api/textures/CompressedTexture" ],
			[ "DataTexture", "api/textures/DataTexture" ],
			[ "Texture", "api/textures/Texture" ]
		],

		"Extras": [
			[ "FontUtils", "api/extras/FontUtils" ],
			[ "GeometryUtils", "api/extras/GeometryUtils" ],
			[ "ImageUtils", "api/extras/ImageUtils" ],
			[ "SceneUtils", "api/extras/SceneUtils" ]
		],

		"Extras / Animation": [
			[ "Animation", "api/extras/animation/Animation" ],
			[ "AnimationHandler", "api/extras/animation/AnimationHandler" ],
			[ "KeyFrameAnimation", "api/extras/animation/KeyFrameAnimation" ]
		],

		"Extras / Core": [
			[ "Curve", "api/extras/core/Curve" ],
			[ "CurvePath", "api/extras/core/CurvePath" ],
			[ "Gyroscope", "api/extras/core/Gyroscope" ],
			[ "Path", "api/extras/core/Path" ],
			[ "Shape", "api/extras/core/Shape" ]
		],

		"Extras / Curves": [
			[ "ArcCurve", "api/extras/curves/ArcCurve" ],
			[ "ClosedSplineCurve3", "api/extras/curves/ClosedSplineCurve3" ],
			[ "CubicBezierCurve", "api/extras/curves/CubicBezierCurve" ],
			[ "CubicBezierCurve3", "api/extras/curves/CubicBezierCurve3" ],
			[ "EllipseCurve", "api/extras/curves/EllipseCurve" ],
			[ "LineCurve", "api/extras/curves/LineCurve" ],
			[ "LineCurve3", "api/extras/curves/LineCurve3" ],
			[ "QuadraticBezierCurve", "api/extras/curves/QuadraticBezierCurve" ],
			[ "QuadraticBezierCurve3", "api/extras/curves/QuadraticBezierCurve3" ],
			[ "SplineCurve", "api/extras/curves/SplineCurve" ],
			[ "SplineCurve3", "api/extras/curves/SplineCurve3" ]
		],

		"Extras / Geometries": [
			[ "BoxGeometry", "api/extras/geometries/BoxGeometry" ],
			[ "CircleGeometry", "api/extras/geometries/CircleGeometry" ],
			[ "CubeGeometry", "api/extras/geometries/CubeGeometry" ],
			[ "CylinderGeometry", "api/extras/geometries/CylinderGeometry" ],
			[ "DodecahedronGeometry", "api/extras/geometries/DodecahedronGeometry" ],
			[ "ExtrudeGeometry", "api/extras/geometries/ExtrudeGeometry" ],
			[ "IcosahedronGeometry", "api/extras/geometries/IcosahedronGeometry" ],
			[ "LatheGeometry", "api/extras/geometries/LatheGeometry" ],
			[ "OctahedronGeometry", "api/extras/geometries/OctahedronGeometry" ],
			[ "ParametricGeometry", "api/extras/geometries/ParametricGeometry" ],
			[ "PlaneGeometry", "api/extras/geometries/PlaneGeometry" ],
			[ "PolyhedronGeometry", "api/extras/geometries/PolyhedronGeometry" ],
			[ "RingGeometry", "api/extras/geometries/RingGeometry" ],
			[ "ShapeGeometry", "api/extras/geometries/ShapeGeometry" ],
			[ "SphereGeometry", "api/extras/geometries/SphereGeometry" ],
			[ "TetrahedronGeometry", "api/extras/geometries/TetrahedronGeometry" ],
			[ "TextGeometry", "api/extras/geometries/TextGeometry" ],
			[ "TorusGeometry", "api/extras/geometries/TorusGeometry" ],
			[ "TorusKnotGeometry", "api/extras/geometries/TorusKnotGeometry" ],
			[ "TubeGeometry", "api/extras/geometries/TubeGeometry" ]
		],

		"Extras / Helpers": [
			[ "ArrowHelper", "api/extras/helpers/ArrowHelper" ],
			[ "AxisHelper", "api/extras/helpers/AxisHelper" ],
			[ "BoundingBoxHelper", "api/extras/helpers/BoundingBoxHelper" ],
			[ "BoxHelper", "api/extras/helpers/BoxHelper" ],
			[ "CameraHelper", "api/extras/helpers/CameraHelper" ],
			[ "DirectionalLightHelper", "api/extras/helpers/DirectionalLightHelper" ],
			[ "EdgesHelper", "api/extras/helpers/EdgesHelper" ],
			[ "FaceNormalsHelper", "api/extras/helpers/FaceNormalsHelper" ],
			[ "GridHelper", "api/extras/helpers/GridHelper" ],
			[ "HemisphereLightHelper", "api/extras/helpers/HemisphereLightHelper" ],
			[ "PointLightHelper", "api/extras/helpers/PointLightHelper" ],
			[ "SpotLightHelper", "api/extras/helpers/SpotLightHelper" ],
			[ "VertexNormalsHelper", "api/extras/helpers/VertexNormalsHelper" ],
			[ "WireframeHelper", "api/extras/helpers/WireframeHelper" ]
		],

		"Extras / Objects": [
			[ "ImmediateRenderObject", "api/extras/objects/ImmediateRenderObject" ],
			[ "MorphBlendMesh", "api/extras/objects/MorphBlendMesh" ]
		],

		"Examples" : [
			[ "CombinedCamera", "api/examples/cameras/CombinedCamera" ],
			[ "LookupTable", "api/examples/Lut" ]

		]

	}

};

var pages = {};

for ( var section in list ) {

	pages[ section ] = {};

	for ( var category in list[ section ] ) {

		pages[ section ][ category ] = {};

		for ( var i = 0; i < list[ section ][ category ].length; i ++ ) {

			var page = list[ section ][ category ][ i ];
			pages[ section ][ category ][ page[ 0 ] ] = page[ 1 ];

		}

	}

}
<commit_msg>Move SpriteCanvasMaterial to examples<commit_after>var list = {

	"Manual": {
		"Introduction": [
			[ "Creating a scene", "manual/introduction/Creating-a-scene" ],
			[ "Matrix transformations", "manual/introduction/Matrix-transformations" ]
		]
	},

	"Reference": {
		"Constants": [
			[ "CustomBlendingEquation", "api/constants/CustomBlendingEquations"],
			[ "GLState", "api/constants/GLState"],
			[ "Materials", "api/constants/Materials"],
			[ "ShadowingTypes", "api/constants/ShadowingTypes"],
			[ "Textures", "api/constants/Textures"]
		],

		"Cameras": [
			[ "Camera", "api/cameras/Camera" ],
			[ "CubeCamera", "api/cameras/CubeCamera" ],
			[ "OrthographicCamera", "api/cameras/OrthographicCamera" ],
			[ "PerspectiveCamera", "api/cameras/PerspectiveCamera" ]
		],

		"Core": [
			[ "BufferAttribute", "api/core/BufferAttribute" ],
			[ "BufferGeometry", "api/core/BufferGeometry" ],
			[ "Clock", "api/core/Clock" ],
			[ "EventDispatcher", "api/core/EventDispatcher" ],
			[ "Face3", "api/core/Face3" ],
			[ "Geometry", "api/core/Geometry" ],
			[ "Object3D", "api/core/Object3D" ],
			[ "Raycaster", "api/core/Raycaster" ]
		],

		"Lights": [
			[ "AmbientLight", "api/lights/AmbientLight" ],
			[ "DirectionalLight", "api/lights/DirectionalLight" ],
			[ "HemisphereLight", "api/lights/HemisphereLight" ],
			[ "Light", "api/lights/Light" ],
			[ "PointLight", "api/lights/PointLight" ],
			[ "SpotLight", "api/lights/SpotLight" ]
		],

		"Loaders": [
			[ "BabylonLoader", "api/loaders/BabylonLoader" ],
			[ "BufferGeometryLoader", "api/loaders/BufferGeometryLoader" ],
			[ "Cache", "api/loaders/Cache" ],
			[ "ColladaLoader", "api/loaders/ColladaLoader" ],
			[ "glTFLoader", "api/loaders/glTFLoader" ],
			[ "ImageLoader", "api/loaders/ImageLoader" ],
			[ "JSONLoader", "api/loaders/JSONLoader" ],
			[ "Loader", "api/loaders/Loader" ],
			[ "LoadingManager", "api/loaders/LoadingManager" ],
			[ "MaterialLoader", "api/loaders/MaterialLoader" ],
			[ "MTLLoader", "api/loaders/MTLLoader" ],
			[ "OBJLoader", "api/loaders/OBJLoader" ],
			[ "OBJMTLLoader", "api/loaders/OBJMTLLoader" ],
			[ "ObjectLoader", "api/loaders/ObjectLoader" ],
			[ "PDBLoader", "api/loaders/PDBLoader" ],
			[ "SVGLoader", "api/loaders/SVGLoader" ],
			[ "TextureLoader", "api/loaders/TextureLoader" ],
			[ "TGALoader", "api/loaders/TGALoader" ],
			[ "XHRLoader", "api/loaders/XHRLoader" ]
		],

		"Materials": [
			[ "LineBasicMaterial", "api/materials/LineBasicMaterial" ],
			[ "LineDashedMaterial", "api/materials/LineDashedMaterial" ],
			[ "Material", "api/materials/Material" ],
			[ "MeshBasicMaterial", "api/materials/MeshBasicMaterial" ],
			[ "MeshDepthMaterial", "api/materials/MeshDepthMaterial" ],
			[ "MeshFaceMaterial", "api/materials/MeshFaceMaterial" ],
			[ "MeshLambertMaterial", "api/materials/MeshLambertMaterial" ],
			[ "MeshNormalMaterial", "api/materials/MeshNormalMaterial" ],
			[ "MeshPhongMaterial", "api/materials/MeshPhongMaterial" ],
			[ "PointsMaterial", "api/materials/PointsMaterial" ],
			[ "RawShaderMaterial", "api/materials/RawShaderMaterial" ],
			[ "ShaderMaterial", "api/materials/ShaderMaterial" ],
			[ "SpriteMaterial", "api/materials/SpriteMaterial" ]
		],

		"Math": [
			[ "Box2", "api/math/Box2" ],
			[ "Box3", "api/math/Box3" ],
			[ "Color", "api/math/Color" ],
			[ "Euler", "api/math/Euler" ],
			[ "Frustum", "api/math/Frustum" ],
			[ "Line3", "api/math/Line3" ],
			[ "Math", "api/math/Math" ],
			[ "Matrix3", "api/math/Matrix3" ],
			[ "Matrix4", "api/math/Matrix4" ],
			[ "Plane", "api/math/Plane" ],
			[ "Quaternion", "api/math/Quaternion" ],
			[ "Ray", "api/math/Ray" ],
			[ "Sphere", "api/math/Sphere" ],
			[ "Spline", "api/math/Spline" ],
			[ "Triangle", "api/math/Triangle" ],
			[ "Vector2", "api/math/Vector2" ],
			[ "Vector3", "api/math/Vector3" ],
			[ "Vector4", "api/math/Vector4" ]
		],

		"Objects": [
			[ "Bone", "api/objects/Bone" ],
			[ "LensFlare", "api/objects/LensFlare" ],
			[ "Line", "api/objects/Line" ],
			[ "LineSegments", "api/objects/LineSegments" ],
			[ "LOD", "api/objects/LOD" ],
			[ "Mesh", "api/objects/Mesh" ],
			[ "MorphAnimMesh", "api/objects/MorphAnimMesh" ],
			[ "Points", "api/objects/Points" ],
			[ "SkinnedMesh", "api/objects/SkinnedMesh" ],
			[ "Skeleton", "api/objects/Skeleton" ],
			[ "Sprite", "api/objects/Sprite" ]
		],

		"Renderers": [
			[ "CanvasRenderer", "api/renderers/CanvasRenderer" ],
			[ "WebGLRenderer", "api/renderers/WebGLRenderer" ],
			[ "WebGLRenderTarget", "api/renderers/WebGLRenderTarget" ],
			[ "WebGLRenderTargetCube", "api/renderers/WebGLRenderTargetCube" ]
		],

		"Renderers / Shaders": [
			[ "ShaderChunk", "api/renderers/shaders/ShaderChunk" ],
			[ "ShaderLib", "api/renderers/shaders/ShaderLib" ],
			[ "UniformsLib", "api/renderers/shaders/UniformsLib" ],
			[ "UniformsUtils", "api/renderers/shaders/UniformsUtils" ]
		],

		"Renderers / WebGL": [
			[ "WebGLProgram", "api/renderers/webgl/WebGLProgram" ],
			[ "WebGLShader", "api/renderers/webgl/WebGLShader" ],
			[ "WebGLState", "api/renderers/webgl/WebGLState" ]
		],

		"Renderers / WebGL / Plugins": [
			[ "LensFlarePlugin", "api/renderers/webgl/plugins/LensFlarePlugin" ],
			[ "ShadowMapPlugin", "api/renderers/webgl/plugins/ShadowMapPlugin" ],
			[ "SpritePlugin", "api/renderers/webgl/plugins/SpritePlugin" ]
		],

		"Scenes": [
			[ "Fog", "api/scenes/Fog" ],
			[ "FogExp2", "api/scenes/FogExp2" ],
			[ "Scene", "api/scenes/Scene" ]
		],

		"Textures": [
			[ "CubeTexture", "api/textures/CubeTexture" ],
			[ "CompressedTexture", "api/textures/CompressedTexture" ],
			[ "DataTexture", "api/textures/DataTexture" ],
			[ "Texture", "api/textures/Texture" ]
		],

		"Extras": [
			[ "FontUtils", "api/extras/FontUtils" ],
			[ "GeometryUtils", "api/extras/GeometryUtils" ],
			[ "ImageUtils", "api/extras/ImageUtils" ],
			[ "SceneUtils", "api/extras/SceneUtils" ]
		],

		"Extras / Animation": [
			[ "Animation", "api/extras/animation/Animation" ],
			[ "AnimationHandler", "api/extras/animation/AnimationHandler" ],
			[ "KeyFrameAnimation", "api/extras/animation/KeyFrameAnimation" ]
		],

		"Extras / Core": [
			[ "Curve", "api/extras/core/Curve" ],
			[ "CurvePath", "api/extras/core/CurvePath" ],
			[ "Gyroscope", "api/extras/core/Gyroscope" ],
			[ "Path", "api/extras/core/Path" ],
			[ "Shape", "api/extras/core/Shape" ]
		],

		"Extras / Curves": [
			[ "ArcCurve", "api/extras/curves/ArcCurve" ],
			[ "ClosedSplineCurve3", "api/extras/curves/ClosedSplineCurve3" ],
			[ "CubicBezierCurve", "api/extras/curves/CubicBezierCurve" ],
			[ "CubicBezierCurve3", "api/extras/curves/CubicBezierCurve3" ],
			[ "EllipseCurve", "api/extras/curves/EllipseCurve" ],
			[ "LineCurve", "api/extras/curves/LineCurve" ],
			[ "LineCurve3", "api/extras/curves/LineCurve3" ],
			[ "QuadraticBezierCurve", "api/extras/curves/QuadraticBezierCurve" ],
			[ "QuadraticBezierCurve3", "api/extras/curves/QuadraticBezierCurve3" ],
			[ "SplineCurve", "api/extras/curves/SplineCurve" ],
			[ "SplineCurve3", "api/extras/curves/SplineCurve3" ]
		],

		"Extras / Geometries": [
			[ "BoxGeometry", "api/extras/geometries/BoxGeometry" ],
			[ "CircleGeometry", "api/extras/geometries/CircleGeometry" ],
			[ "CubeGeometry", "api/extras/geometries/CubeGeometry" ],
			[ "CylinderGeometry", "api/extras/geometries/CylinderGeometry" ],
			[ "DodecahedronGeometry", "api/extras/geometries/DodecahedronGeometry" ],
			[ "ExtrudeGeometry", "api/extras/geometries/ExtrudeGeometry" ],
			[ "IcosahedronGeometry", "api/extras/geometries/IcosahedronGeometry" ],
			[ "LatheGeometry", "api/extras/geometries/LatheGeometry" ],
			[ "OctahedronGeometry", "api/extras/geometries/OctahedronGeometry" ],
			[ "ParametricGeometry", "api/extras/geometries/ParametricGeometry" ],
			[ "PlaneGeometry", "api/extras/geometries/PlaneGeometry" ],
			[ "PolyhedronGeometry", "api/extras/geometries/PolyhedronGeometry" ],
			[ "RingGeometry", "api/extras/geometries/RingGeometry" ],
			[ "ShapeGeometry", "api/extras/geometries/ShapeGeometry" ],
			[ "SphereGeometry", "api/extras/geometries/SphereGeometry" ],
			[ "TetrahedronGeometry", "api/extras/geometries/TetrahedronGeometry" ],
			[ "TextGeometry", "api/extras/geometries/TextGeometry" ],
			[ "TorusGeometry", "api/extras/geometries/TorusGeometry" ],
			[ "TorusKnotGeometry", "api/extras/geometries/TorusKnotGeometry" ],
			[ "TubeGeometry", "api/extras/geometries/TubeGeometry" ]
		],

		"Extras / Helpers": [
			[ "ArrowHelper", "api/extras/helpers/ArrowHelper" ],
			[ "AxisHelper", "api/extras/helpers/AxisHelper" ],
			[ "BoundingBoxHelper", "api/extras/helpers/BoundingBoxHelper" ],
			[ "BoxHelper", "api/extras/helpers/BoxHelper" ],
			[ "CameraHelper", "api/extras/helpers/CameraHelper" ],
			[ "DirectionalLightHelper", "api/extras/helpers/DirectionalLightHelper" ],
			[ "EdgesHelper", "api/extras/helpers/EdgesHelper" ],
			[ "FaceNormalsHelper", "api/extras/helpers/FaceNormalsHelper" ],
			[ "GridHelper", "api/extras/helpers/GridHelper" ],
			[ "HemisphereLightHelper", "api/extras/helpers/HemisphereLightHelper" ],
			[ "PointLightHelper", "api/extras/helpers/PointLightHelper" ],
			[ "SpotLightHelper", "api/extras/helpers/SpotLightHelper" ],
			[ "VertexNormalsHelper", "api/extras/helpers/VertexNormalsHelper" ],
			[ "WireframeHelper", "api/extras/helpers/WireframeHelper" ]
		],

		"Extras / Objects": [
			[ "ImmediateRenderObject", "api/extras/objects/ImmediateRenderObject" ],
			[ "MorphBlendMesh", "api/extras/objects/MorphBlendMesh" ]
		],

		"Examples" : [
			[ "CombinedCamera", "api/examples/cameras/CombinedCamera" ],
			[ "LookupTable", "api/examples/Lut" ],
			[ "SpriteCanvasMaterial", "api/materials/SpriteCanvasMaterial" ]

		]

	}

};

var pages = {};

for ( var section in list ) {

	pages[ section ] = {};

	for ( var category in list[ section ] ) {

		pages[ section ][ category ] = {};

		for ( var i = 0; i < list[ section ][ category ].length; i ++ ) {

			var page = list[ section ][ category ][ i ];
			pages[ section ][ category ][ page[ 0 ] ] = page[ 1 ];

		}

	}

}
<|endoftext|>