<commit_before>'use strict';

var logger = require('log4js').getLogger('blueprints_spec');
var components = require('../../src/components/index');

describe('blueprints page', function(){
    var testConf = components.config.tests.sanity.blueprints_spec;
    var BlueprintPage = components.ui.blueprints.BlueprintPage;

    beforeEach(function(){ logger.info('running from ' + __filename); });

    beforeEach(function(){
        components.ui.LoginPage.goTo().login('admin', 'admin');
    });

    describe('blueprint view', function(){

        beforeEach(function(){
            components.ui.blueprints.IndexPage.goToBlueprint({'name' : testConf.blueprints.blueprintToRead });
        });

        it('should have all sections', function (done) {
            components.ui.blueprints.BlueprintPage.goToNodes();
            components.ui.blueprints.BlueprintPage.goToSource();
            browser.sleep(1000).then( done );
        });

        it('should have working source section', function(done) {

            logger.trace('start blueprint source section test');
            BlueprintPage.goToSource();
            expect(BlueprintPage.Source.getLoadingMessage()).toBe('Generating Blueprint Source View...');
            BlueprintPage.Source.getTree();
            // we should be viewing the main blueprint file:
            BlueprintPage.Source.getFileTitle('simple-blueprint.yaml');
            BlueprintPage.Source.selectFile('README.md');
            BlueprintPage.Source.getFileTitle('README.md');
            BlueprintPage.Source.getFileContent();

            browser.sleep(1000).then( done );
        });

    });

    describe('blueprints page operations', function(){
        it('should create a deployment, CREATE_DEPLOYMENT_EXISTS', function (done) {
            logger.trace('start create deployment test');

            components.ui.blueprints.IndexPage.createDeployment({'name' : testConf.blueprints.blueprintToDeploy});

            var newDeploymentName = 'new-deployment-' + new Date().getTime();

            components.ui.common.Dialog.clickClose(); // lets test cancel on the way!

            function createDeployment( waitFor ) {

                components.ui.blueprints.IndexPage.createDeployment({'name': testConf.blueprints.blueprintToDeploy});
                components.ui.blueprints.CreateDeployment.setDetails({
                    name: newDeploymentName,
                    raw: components.config.tests.sanity.blueprints_spec.deployment
                });
                components.ui.blueprints.CreateDeployment.deploy();
                if ( waitFor ) {
                    components.ui.deployments.DeploymentPage.waitForInitializingToStop();
                }
            }

            createDeployment(true);

            browser.sleep(3000);
            expect(browser.getCurrentUrl()).toContain('/deployment/', 'DEPLOY_REDIRECT should redirect to deployments');

            components.ui.layout.goToBlueprints();

            createDeployment(false);

            expect(components.ui.common.Dialog.getErrorMessage()).toContain('already', 'CFY-2902 duplicate name should cause error');
            components.ui.common.Dialog.close();

            components.ui.layout.goToDeployments();
            components.ui.deployments.IndexPage.deleteDeployment({id:newDeploymentName});

            browser.sleep(1000).then(done);
        });

        it('should show deploy dialog after default button pressed', function (done) {

            logger.trace('start create deployment with default button test');

            components.ui.blueprints.IndexPage.createDeploymentWithDefaultBtn({'name' : testConf.blueprints.blueprintToDeploy});
            expect(element(by.css('#deployDialogContainer')).isPresent()).toBe(true);

            browser.sleep(500).then(function(){ done(); });
        });

        it('should upload and delete a blueprint, ENTER_BLUEPRINT_NAME', function(done){
            // todo
            //done();
            var blueprintName = 'uploaded-' + new Date().getTime();
            var uploadedBlueprint = { blueprint_id: blueprintName , 'blueprint_filename' : 'simple-blueprint.yaml', 'blueprint_location':'https://github.com/cloudify-cosmo/cloudify-nodecellar-example/archive/master.zip' };
            components.ui.blueprints.IndexPage.uploadBlueprint();

            components.ui.common.Dialog.clickClose();

            expect(components.ui.blueprints.UploadBlueprintDialog.getUploadForm().isPresent()).toBe(false,'CLOSE_BLUEPRINT_DIALOG dialog should be closed after');
            components.ui.blueprints.IndexPage.uploadBlueprint(); // reopen

            components.ui.blueprints.UploadBlueprintDialog.getBlueprintIdLabel().then(function(label){
                expect(label.toLowerCase()).toContain('blueprint id', 'CFY-2301 label should be blueprint id ...  also verifies dialog is open');
            });
            components.ui.blueprints.UploadBlueprintDialog.setDetails(uploadedBlueprint);
            components.ui.blueprints.UploadBlueprintDialog.submit();

            expect(browser.getCurrentUrl()).toContain(uploadedBlueprint.blueprint_id,'UPLOAD_REDIRECT upload should redirect to blueprint when finished');
            //components.ui.blueprints.IndexPage.waitForUploadDone({name: uploadedBlueprint.blueprint_id});
            components.ui.layout.goToBlueprints();

            components.ui.blueprints.IndexPage.deleteBlueprint({name:blueprintName});
            expect(components.ui.blueprints.IndexPage.getBlueprint({name: blueprintName }, true)).toBeUndefined('CFY-3524 delete blueprint should refresh blueprints');

            browser.sleep(1000).then(function(){ done(); });
        });

    });

    describe('topology view', function(){
        it('should open node details panel when clicking on node in topology section', function(done) {
            logger.trace('start blueprint topology section test');
            components.ui.blueprints.IndexPage.goToBlueprint({'name' : testConf.blueprints.blueprintToRead});
            components.ui.blueprints.BlueprintPage.goToTopology();
            components.ui.blueprints.BlueprintPage.Topology.clickNode(testConf.blueprints.nodeToClick);
            components.ui.blueprints.BlueprintPage.PropertiesPanel.waitForVisibility();
            components.ui.blueprints.BlueprintPage.PropertiesPanel.goToProperties();
            components.ui.blueprints.BlueprintPage.PropertiesPanel.goToRelationships();
            components.ui.blueprints.BlueprintPage.PropertiesPanel.close();

            browser.sleep(1000).then(function(){ done(); });
        });
    });

    //it('should open node details panel when clicking on node in network section', function (done) {
    //    logger.trace('start blueprint network section test');
    //    components.ui.blueprints.IndexPage.goToBlueprint({'name' : DEPLOY_BLUEPRINT_NAME});
    //    components.ui.blueprints.BlueprintPage.goToSection('Network');
    //    components.ui.blueprints.BlueprintPage.Network.getSubnets();
    //    components.ui.blueprints.BlueprintPage.Network.getRouters();
    //
    //    browser.sleep(1000).then(function(){ done(); });
    //});
    //
    //it('should open node details panel when clicking on node in nodes section', function (done) {
    //    logger.trace('start blueprint node section test');
    //    components.ui.blueprints.IndexPage.goToBlueprint({'name' : DEPLOY_BLUEPRINT_NAME});
    //    components.ui.blueprints.BlueprintPage.goToSection('Nodes');
    //    components.ui.blueprints.BlueprintPage.NodesTable.clickNode('mongod');
    //
    //    browser.sleep(1000).then(function(){ done(); });
    //});

});
<commit_msg>exclude tests<commit_after>'use strict';

var logger = require('log4js').getLogger('blueprints_spec');
var components = require('../../src/components/index');

describe('blueprints page', function(){
    var testConf = components.config.tests.sanity.blueprints_spec;
    var BlueprintPage = components.ui.blueprints.BlueprintPage;

    beforeEach(function(){ logger.info('running from ' + __filename); });

    beforeEach(function(){
        components.ui.LoginPage.goTo().login('admin', 'admin');
    });

    describe('blueprint view', function(){

        beforeEach(function(){
            components.ui.blueprints.IndexPage.goToBlueprint({'name' : testConf.blueprints.blueprintToRead });
        });

        xit('should have all sections', function (done) {
            components.ui.blueprints.BlueprintPage.goToNodes();
            components.ui.blueprints.BlueprintPage.goToSource();
            browser.sleep(1000).then( done );
        });

        it('should have working source section', function(done) {

            logger.trace('start blueprint source section test');
            BlueprintPage.goToSource();
            expect(BlueprintPage.Source.getLoadingMessage()).toBe('Generating Blueprint Source View...');
            BlueprintPage.Source.getTree();
            // we should be viewing the main blueprint file:
            BlueprintPage.Source.getFileTitle('simple-blueprint.yaml');
            BlueprintPage.Source.selectFile('README.md');
            BlueprintPage.Source.getFileTitle('README.md');
            BlueprintPage.Source.getFileContent();

            browser.sleep(1000).then( done );
        });

    });

    xdescribe('blueprints page operations', function(){
        it('should create a deployment, CREATE_DEPLOYMENT_EXISTS', function (done) {
            logger.trace('start create deployment test');

            components.ui.blueprints.IndexPage.createDeployment({'name' : testConf.blueprints.blueprintToDeploy});

            var newDeploymentName = 'new-deployment-' + new Date().getTime();

            components.ui.common.Dialog.clickClose(); // lets test cancel on the way!

            function createDeployment( waitFor ) {

                components.ui.blueprints.IndexPage.createDeployment({'name': testConf.blueprints.blueprintToDeploy});
                components.ui.blueprints.CreateDeployment.setDetails({
                    name: newDeploymentName,
                    raw: components.config.tests.sanity.blueprints_spec.deployment
                });
                components.ui.blueprints.CreateDeployment.deploy();
                if ( waitFor ) {
                    components.ui.deployments.DeploymentPage.waitForInitializingToStop();
                }
            }

            createDeployment(true);

            browser.sleep(3000);
            expect(browser.getCurrentUrl()).toContain('/deployment/', 'DEPLOY_REDIRECT should redirect to deployments');

            components.ui.layout.goToBlueprints();

            createDeployment(false);

            expect(components.ui.common.Dialog.getErrorMessage()).toContain('already', 'CFY-2902 duplicate name should cause error');
            components.ui.common.Dialog.close();

            components.ui.layout.goToDeployments();
            components.ui.deployments.IndexPage.deleteDeployment({id:newDeploymentName});

            browser.sleep(1000).then(done);
        });

        it('should show deploy dialog after default button pressed', function (done) {

            logger.trace('start create deployment with default button test');

            components.ui.blueprints.IndexPage.createDeploymentWithDefaultBtn({'name' : testConf.blueprints.blueprintToDeploy});
            expect(element(by.css('#deployDialogContainer')).isPresent()).toBe(true);

            browser.sleep(500).then(function(){ done(); });
        });

        it('should upload and delete a blueprint, ENTER_BLUEPRINT_NAME', function(done){
            // todo
            //done();
            var blueprintName = 'uploaded-' + new Date().getTime();
            var uploadedBlueprint = { blueprint_id: blueprintName , 'blueprint_filename' : 'simple-blueprint.yaml', 'blueprint_location':'https://github.com/cloudify-cosmo/cloudify-nodecellar-example/archive/master.zip' };
            components.ui.blueprints.IndexPage.uploadBlueprint();

            components.ui.common.Dialog.clickClose();

            expect(components.ui.blueprints.UploadBlueprintDialog.getUploadForm().isPresent()).toBe(false,'CLOSE_BLUEPRINT_DIALOG dialog should be closed after');
            components.ui.blueprints.IndexPage.uploadBlueprint(); // reopen

            components.ui.blueprints.UploadBlueprintDialog.getBlueprintIdLabel().then(function(label){
                expect(label.toLowerCase()).toContain('blueprint id', 'CFY-2301 label should be blueprint id ...  also verifies dialog is open');
            });
            components.ui.blueprints.UploadBlueprintDialog.setDetails(uploadedBlueprint);
            components.ui.blueprints.UploadBlueprintDialog.submit();

            expect(browser.getCurrentUrl()).toContain(uploadedBlueprint.blueprint_id,'UPLOAD_REDIRECT upload should redirect to blueprint when finished');
            //components.ui.blueprints.IndexPage.waitForUploadDone({name: uploadedBlueprint.blueprint_id});
            components.ui.layout.goToBlueprints();

            components.ui.blueprints.IndexPage.deleteBlueprint({name:blueprintName});
            expect(components.ui.blueprints.IndexPage.getBlueprint({name: blueprintName }, true)).toBeUndefined('CFY-3524 delete blueprint should refresh blueprints');

            browser.sleep(1000).then(function(){ done(); });
        });

    });

    xdescribe('topology view', function(){
        it('should open node details panel when clicking on node in topology section', function(done) {
            logger.trace('start blueprint topology section test');
            components.ui.blueprints.IndexPage.goToBlueprint({'name' : testConf.blueprints.blueprintToRead});
            components.ui.blueprints.BlueprintPage.goToTopology();
            components.ui.blueprints.BlueprintPage.Topology.clickNode(testConf.blueprints.nodeToClick);
            components.ui.blueprints.BlueprintPage.PropertiesPanel.waitForVisibility();
            components.ui.blueprints.BlueprintPage.PropertiesPanel.goToProperties();
            components.ui.blueprints.BlueprintPage.PropertiesPanel.goToRelationships();
            components.ui.blueprints.BlueprintPage.PropertiesPanel.close();

            browser.sleep(1000).then(function(){ done(); });
        });
    });

    //it('should open node details panel when clicking on node in network section', function (done) {
    //    logger.trace('start blueprint network section test');
    //    components.ui.blueprints.IndexPage.goToBlueprint({'name' : DEPLOY_BLUEPRINT_NAME});
    //    components.ui.blueprints.BlueprintPage.goToSection('Network');
    //    components.ui.blueprints.BlueprintPage.Network.getSubnets();
    //    components.ui.blueprints.BlueprintPage.Network.getRouters();
    //
    //    browser.sleep(1000).then(function(){ done(); });
    //});
    //
    //it('should open node details panel when clicking on node in nodes section', function (done) {
    //    logger.trace('start blueprint node section test');
    //    components.ui.blueprints.IndexPage.goToBlueprint({'name' : DEPLOY_BLUEPRINT_NAME});
    //    components.ui.blueprints.BlueprintPage.goToSection('Nodes');
    //    components.ui.blueprints.BlueprintPage.NodesTable.clickNode('mongod');
    //
    //    browser.sleep(1000).then(function(){ done(); });
    //});

});
<|endoftext|>


<commit_before>import $ from 'blingblingjs'
import hotkeys from 'hotkeys-js'

import * as Icons from './toolpallete.icons'
import {
  Selectable, Moveable, Padding, Margin, EditText, Font,
  Flex, Search, ColorPicker, BoxShadow, HueShift, MetaTip,
  Guides, Screenshot, Position, Accessibility
} from '../features/'

import { provideSelectorEnginer } from '../features/search'

export default class ToolPallete extends HTMLElement {
  constructor() {
    super()

    this.toolbar_model = {
      g: { tool: 'guides', icon: Icons.ruler, label: 'Guides', description: 'Verify alignment' },
      i: { tool: 'inspector', icon: Icons.inspector, label: 'Inspect', description: 'Peak into the common & current styles of an element' },
      x: { tool: 'accessibility', icon: Icons.accessibility, label: 'Accessibility', description: 'Peak into a11y attributes & compliance status' },
      v: { tool: 'move', icon: Icons.move, label: 'Move', description: 'Push elements in, out & around' },
      // r: { tool: 'resize', icon: Icons.resize, label: 'Resize', description: '' },
      m: { tool: 'margin', icon: Icons.margin, label: 'Margin', description: 'Add or subtract outer space' },
      p: { tool: 'padding', icon: Icons.padding, label: 'Padding', description: 'Add or subtract inner space' },
      // b: { tool: 'border', icon: Icons.border, label: 'Border', description: '' },
      a: { tool: 'align', icon: Icons.align, label: 'Flexbox Align', description: 'Create or modify direction, distribution & alignment' },
      h: { tool: 'hueshift', icon: Icons.hueshift, label: 'Hue Shift', description: 'Change fg/bg hue, brightness, saturation & opacity' },
      d: { tool: 'boxshadow', icon: Icons.boxshadow, label: 'Shadow', description: 'Create & adjust position, blur & opacity of a box shadow' },
      // t: { tool: 'transform', icon: Icons.transform, label: '3D Transform', description: '' },
      l: { tool: 'position', icon: Icons.position, label: 'Position', description: 'Move svg (x,y) and elements (top,left,bottom,right)' },
      f: { tool: 'font', icon: Icons.font, label: 'Font Styles', description: 'Change size, leading, kerning, & weight' },
      e: { tool: 'text', icon: Icons.type, label: 'Edit Text', description: 'Change any text on the page' },
      c: { tool: 'screenshot', icon: Icons.camera, label: 'Screenshot', description: 'Screenshot selected elements or the entire page' },
      s: { tool: 'search', icon: Icons.search, label: 'Search', description: 'Select elements by searching for them' },
    }

    this.$shadow = this.attachShadow({mode: 'open'})
    this.$shadow.innerHTML = this.render()

    this.selectorEngine = Selectable()
    this.colorPicker    = ColorPicker(this.$shadow, this.selectorEngine)
    provideSelectorEnginer(this.selectorEngine)
  }

  connectedCallback() {
    $('li[data-tool]', this.$shadow).on('click', e =>
      this.toolSelected(e.currentTarget) && e.stopPropagation())

    Object.entries(this.toolbar_model).forEach(([key, value]) =>
      hotkeys(key, e =>
        this.toolSelected(
          $(`[data-tool="${value.tool}"]`, this.$shadow)[0])))

    this.toolSelected($('[data-tool="guides"]', this.$shadow)[0])
  }

  disconnectedCallback() {
    this.deactivate_feature()
    this.selectorEngine.disconnect()
    hotkeys.unbind(
      Object.keys(this.toolbar_model).reduce((events, key) =>
        events += ',' + key, ''))
  }

  toolSelected(el) {
    if (typeof el === 'string')
      el = $(`[data-tool="${el}"]`, this.$shadow)[0]

    if (this.active_tool && this.active_tool.dataset.tool === el.dataset.tool) return

    if (this.active_tool) {
      this.active_tool.attr('data-active', null)
      this.deactivate_feature()
    }

    el.attr('data-active', true)
    this.active_tool = el
    this[el.dataset.tool]()
  }

  render() {
    return `
      ${this.styles()}

        ${Object.entries(this.toolbar_model).reduce((list, [key, value]) => `
          ${list}

${value.icon}

        `,'')}

          <input type="color" value="">
          ${Icons.color_text}

          <input type="color" value="">
          ${Icons.color_background}

          <input type="color" value="">
          ${Icons.color_border}

    `
  }

  styles() {
    return `

    `
  }

  move() {
    this.deactivate_feature = Moveable('[data-selected=true]')
  }

  margin() {
    this.deactivate_feature = Margin('[data-selected=true]')
  }

  padding() {
    this.deactivate_feature = Padding('[data-selected=true]')
  }

  font() {
    this.deactivate_feature = Font('[data-selected=true]')
  }

  text() {
    this.selectorEngine.onSelectedUpdate(EditText)
    this.deactivate_feature = () =>
      this.selectorEngine.removeSelectedCallback(EditText)
  }

  align() {
    this.deactivate_feature = Flex('[data-selected=true]')
  }

  search() {
    this.deactivate_feature = Search($('[data-tool="search"]', this.$shadow))
  }

  boxshadow() {
    this.deactivate_feature = BoxShadow('[data-selected=true]')
  }

  hueshift() {
    let feature = HueShift(this.colorPicker)
    this.selectorEngine.onSelectedUpdate(feature.onNodesSelected)
    this.deactivate_feature = () => {
      this.selectorEngine.removeSelectedCallback(feature.onNodesSelected)
      feature.disconnect()
    }
  }

  inspector() {
    this.deactivate_feature = MetaTip()
  }

  accessibility() {
    this.deactivate_feature = Accessibility()
  }

  guides() {
    this.deactivate_feature = Guides()
  }

  screenshot() {
    this.deactivate_feature = Screenshot()
  }

  position() {
    let feature = Position()
    this.selectorEngine.onSelectedUpdate(feature.onNodesSelected)
    this.deactivate_feature = () => {
      this.selectorEngine.removeSelectedCallback(feature.onNodesSelected)
      feature.disconnect()
    }
  }

  get activeTool() {
    return this.active_tool.dataset.tool
  }
}

customElements.define('tool-pallete', ToolPallete)<commit_msg>remove camera icon for now<commit_after>import $ from 'blingblingjs'
import hotkeys from 'hotkeys-js'

import * as Icons from './toolpallete.icons'
import {
  Selectable, Moveable, Padding, Margin, EditText, Font,
  Flex, Search, ColorPicker, BoxShadow, HueShift, MetaTip,
  Guides, Screenshot, Position, Accessibility
} from '../features/'

import { provideSelectorEnginer } from '../features/search'

export default class ToolPallete extends HTMLElement {
  constructor() {
    super()

    this.toolbar_model = {
      g: { tool: 'guides', icon: Icons.ruler, label: 'Guides', description: 'Verify alignment' },
      i: { tool: 'inspector', icon: Icons.inspector, label: 'Inspect', description: 'Peak into the common & current styles of an element' },
      x: { tool: 'accessibility', icon: Icons.accessibility, label: 'Accessibility', description: 'Peak into a11y attributes & compliance status' },
      v: { tool: 'move', icon: Icons.move, label: 'Move', description: 'Push elements in, out & around' },
      // r: { tool: 'resize', icon: Icons.resize, label: 'Resize', description: '' },
      m: { tool: 'margin', icon: Icons.margin, label: 'Margin', description: 'Add or subtract outer space' },
      p: { tool: 'padding', icon: Icons.padding, label: 'Padding', description: 'Add or subtract inner space' },
      // b: { tool: 'border', icon: Icons.border, label: 'Border', description: '' },
      a: { tool: 'align', icon: Icons.align, label: 'Flexbox Align', description: 'Create or modify direction, distribution & alignment' },
      h: { tool: 'hueshift', icon: Icons.hueshift, label: 'Hue Shift', description: 'Change fg/bg hue, brightness, saturation & opacity' },
      d: { tool: 'boxshadow', icon: Icons.boxshadow, label: 'Shadow', description: 'Create & adjust position, blur & opacity of a box shadow' },
      // t: { tool: 'transform', icon: Icons.transform, label: '3D Transform', description: '' },
      l: { tool: 'position', icon: Icons.position, label: 'Position', description: 'Move svg (x,y) and elements (top,left,bottom,right)' },
      f: { tool: 'font', icon: Icons.font, label: 'Font Styles', description: 'Change size, leading, kerning, & weight' },
      e: { tool: 'text', icon: Icons.type, label: 'Edit Text', description: 'Change any text on the page' },
      // c: { tool: 'screenshot', icon: Icons.camera, label: 'Screenshot', description: 'Screenshot selected elements or the entire page' },
      s: { tool: 'search', icon: Icons.search, label: 'Search', description: 'Select elements by searching for them' },
    }

    this.$shadow = this.attachShadow({mode: 'open'})
    this.$shadow.innerHTML = this.render()

    this.selectorEngine = Selectable()
    this.colorPicker    = ColorPicker(this.$shadow, this.selectorEngine)
    provideSelectorEnginer(this.selectorEngine)
  }

  connectedCallback() {
    $('li[data-tool]', this.$shadow).on('click', e =>
      this.toolSelected(e.currentTarget) && e.stopPropagation())

    Object.entries(this.toolbar_model).forEach(([key, value]) =>
      hotkeys(key, e =>
        this.toolSelected(
          $(`[data-tool="${value.tool}"]`, this.$shadow)[0])))

    this.toolSelected($('[data-tool="guides"]', this.$shadow)[0])
  }

  disconnectedCallback() {
    this.deactivate_feature()
    this.selectorEngine.disconnect()
    hotkeys.unbind(
      Object.keys(this.toolbar_model).reduce((events, key) =>
        events += ',' + key, ''))
  }

  toolSelected(el) {
    if (typeof el === 'string')
      el = $(`[data-tool="${el}"]`, this.$shadow)[0]

    if (this.active_tool && this.active_tool.dataset.tool === el.dataset.tool) return

    if (this.active_tool) {
      this.active_tool.attr('data-active', null)
      this.deactivate_feature()
    }

    el.attr('data-active', true)
    this.active_tool = el
    this[el.dataset.tool]()
  }

  render() {
    return `
      ${this.styles()}

        ${Object.entries(this.toolbar_model).reduce((list, [key, value]) => `
          ${list}

${value.icon}

        `,'')}

          <input type="color" value="">
          ${Icons.color_text}

          <input type="color" value="">
          ${Icons.color_background}

          <input type="color" value="">
          ${Icons.color_border}

    `
  }

  styles() {
    return `

    `
  }

  move() {
    this.deactivate_feature = Moveable('[data-selected=true]')
  }

  margin() {
    this.deactivate_feature = Margin('[data-selected=true]')
  }

  padding() {
    this.deactivate_feature = Padding('[data-selected=true]')
  }

  font() {
    this.deactivate_feature = Font('[data-selected=true]')
  }

  text() {
    this.selectorEngine.onSelectedUpdate(EditText)
    this.deactivate_feature = () =>
      this.selectorEngine.removeSelectedCallback(EditText)
  }

  align() {
    this.deactivate_feature = Flex('[data-selected=true]')
  }

  search() {
    this.deactivate_feature = Search($('[data-tool="search"]', this.$shadow))
  }

  boxshadow() {
    this.deactivate_feature = BoxShadow('[data-selected=true]')
  }

  hueshift() {
    let feature = HueShift(this.colorPicker)
    this.selectorEngine.onSelectedUpdate(feature.onNodesSelected)
    this.deactivate_feature = () => {
      this.selectorEngine.removeSelectedCallback(feature.onNodesSelected)
      feature.disconnect()
    }
  }

  inspector() {
    this.deactivate_feature = MetaTip()
  }

  accessibility() {
    this.deactivate_feature = Accessibility()
  }

  guides() {
    this.deactivate_feature = Guides()
  }

  screenshot() {
    this.deactivate_feature = Screenshot()
  }

  position() {
    let feature = Position()
    this.selectorEngine.onSelectedUpdate(feature.onNodesSelected)
    this.deactivate_feature = () => {
      this.selectorEngine.removeSelectedCallback(feature.onNodesSelected)
      feature.disconnect()
    }
  }

  get activeTool() {
    return this.active_tool.dataset.tool
  }
}

customElements.define('tool-pallete', ToolPallete)<|endoftext|>


<commit_before>import React from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import feather from 'feather-icons';
import { Helmet } from 'react-helmet';

import LibraryList from '../../components/LibraryList';
import injectReducer from '../../utils/injectReducer';
import reducer from './reducer';
import {
    prepareMoviesForDashboard,
    favoriteMovie,
    unFavoriteMovie,
    prepareMovieGenres
} from './thunks';
import FixedActionButton from '../../components/FixedActionButton';
import { STANDARD_SEARCH_TYPE } from '../../containers/SearchResults/constants';
import Header from '../../components/Header';
import SearchResults from '../SearchResults';
import MobileNav from '../../components/MobileNav';
import SubMenu from '../../components/SubMenu';
import { showSearch, hideSearch } from '../SearchResults/actions';
import {
    prepareRecentFormats
} from '../App/thunks';
import {
    prepareSearchResults
} from '../SearchResults/thunks';
import { openMobileNav, closeMobileNav } from './actions';
import {
    LOGIN_URL,
    DASHBOARD_URL
} from '../../common/constants';
import { logOutUser } from '../../common/auth/thunks';

class Dashboard extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
    static propTypes = {
        library: PropTypes.array,
        onLoad: PropTypes.func,
        searchIsVisible: PropTypes.bool.isRequired,
        searchResultsAreVisible: PropTypes.bool,
        showSearchHandler: PropTypes.func.isRequired,
        hideSearchHandler: PropTypes.func.isRequired,
        searchType: PropTypes.string,
        submitSearchHandler: PropTypes.func.isRequired,
        openMobileNavHandler: PropTypes.func,
        closeMobileNavHandler: PropTypes.func,
        mobileNavIsOpen: PropTypes.bool,
        logOutHandler: PropTypes.func,
        toggleFavoriteHandler: PropTypes.func,
        subMenuItems: PropTypes.array,
        refreshDashboard: PropTypes.func.isRequired,
    };

    static defaultProps = {
        subMenuItems: [],
    };

    componentDidMount() {
        this.props.onLoad();
    }

    componentDidUpdate() {
        this.props.refreshDashboard();
    }

    render() {
        const {
            library,
            showSearchHandler,
            searchIsVisible,
            hideSearchHandler,
            searchType,
            submitSearchHandler,
            searchResultsAreVisible,
            openMobileNavHandler,
            closeMobileNavHandler,
            mobileNavIsOpen,
            logOutHandler,
            toggleFavoriteHandler,
            subMenuItems,
        } = this.props;
        let pageContent;

        if (searchResultsAreVisible) {
            pageContent = <SearchResults />
        } else {
            pageContent = (

                    <LibraryList items={library} favoriteHandler={toggleFavoriteHandler} />
                    <FixedActionButton clickHandler={() => showSearchHandler(STANDARD_SEARCH_TYPE)} />

            );
        }

        return (

                <Helmet>
                    <title>Dashboard | MMDb</title>
                    <meta name="description" content="My movie database" />
                </Helmet>

                <MobileNav
                    open={mobileNavIsOpen}
                    closeHandler={closeMobileNavHandler}
                    navItems={[
                        { display: 'Home', url: DASHBOARD_URL, icon: feather.icons.home },
                        { display: 'Logout', url: LOGIN_URL, onClickHandler: logOutHandler, icon: feather.icons['log-out'] },
                    ]}
                />
                 submitSearchHandler(query, searchType)}
                    openMobileNavHandler={openMobileNavHandler}
                />
                <SubMenu items={subMenuItems} />

                {pageContent}

        );
    }
}

const withConnect = connect(
    (state) => ({
        searchResultsAreVisible: state.search.resultsAreVisible,
        searchIsVisible: state.search.isVisible,
        searchType: state.search.searchType,
        library: state.dashboard.library,
        mobileNavIsOpen: state.dashboard.mobileNavIsOpen,
        subMenuItems: state.dashboard.subMenu,
    }),
    (dispatch, { location }) => ({
        onLoad: () => {
            Promise.all([
                dispatch(prepareMoviesForDashboard(location)),
                dispatch(prepareMovieGenres()),
            ]).then(() => dispatch(prepareRecentFormats()));
        },
        refreshDashboard: () => {
            dispatch(prepareMoviesForDashboard(location));
        },
        showSearchHandler: (type) => dispatch(showSearch(type)),
        hideSearchHandler: () => {
            dispatch(hideSearch());
            dispatch(prepareMoviesForDashboard());
        },
        submitSearchHandler: (query, searchType) => {
            dispatch(prepareSearchResults(query, searchType));
        },
        openMobileNavHandler: () => {
            dispatch(openMobileNav());
        },
        closeMobileNavHandler: () => {
            dispatch(closeMobileNav());
        },
        logOutHandler: () => {
            dispatch(closeMobileNav());
            dispatch(logOutUser());
        },
        toggleFavoriteHandler: (movie) => {
            const { isFavorite } = movie;

            if (isFavorite) {
                dispatch(unFavoriteMovie(movie))
            } else {
                dispatch(favoriteMovie(movie));
            }
        },
    })
);

const withReducer = injectReducer({ key: 'dashboard', reducer });

export default compose(
    withReducer,
    withConnect,
)(Dashboard);
<commit_msg>Add padding to bottom of library list to make accessible above fixed button at bottom of screen<commit_after>import React from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import feather from 'feather-icons';
import { Helmet } from 'react-helmet';
import styled from 'styled-components';

import LibraryList from '../../components/LibraryList';
import injectReducer from '../../utils/injectReducer';
import reducer from './reducer';
import {
    prepareMoviesForDashboard,
    favoriteMovie,
    unFavoriteMovie,
    prepareMovieGenres
} from './thunks';
import FixedActionButton from '../../components/FixedActionButton';
import { STANDARD_SEARCH_TYPE } from '../../containers/SearchResults/constants';
import Header from '../../components/Header';
import SearchResults from '../SearchResults';
import MobileNav from '../../components/MobileNav';
import SubMenu from '../../components/SubMenu';
import { showSearch, hideSearch } from '../SearchResults/actions';
import {
    prepareRecentFormats
} from '../App/thunks';
import {
    prepareSearchResults
} from '../SearchResults/thunks';
import { openMobileNav, closeMobileNav } from './actions';
import {
    LOGIN_URL,
    DASHBOARD_URL
} from '../../common/constants';
import { logOutUser } from '../../common/auth/thunks';

const LibraryContainer = styled.div`
    padding-bottom: 50px;
`;

class Dashboard extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
    static propTypes = {
        library: PropTypes.array,
        onLoad: PropTypes.func,
        searchIsVisible: PropTypes.bool.isRequired,
        searchResultsAreVisible: PropTypes.bool,
        showSearchHandler: PropTypes.func.isRequired,
        hideSearchHandler: PropTypes.func.isRequired,
        searchType: PropTypes.string,
        submitSearchHandler: PropTypes.func.isRequired,
        openMobileNavHandler: PropTypes.func,
        closeMobileNavHandler: PropTypes.func,
        mobileNavIsOpen: PropTypes.bool,
        logOutHandler: PropTypes.func,
        toggleFavoriteHandler: PropTypes.func,
        subMenuItems: PropTypes.array,
        refreshDashboard: PropTypes.func.isRequired,
    };

    static defaultProps = {
        subMenuItems: [],
    };

    componentDidMount() {
        this.props.onLoad();
    }

    componentDidUpdate() {
        this.props.refreshDashboard();
    }

    render() {
        const {
            library,
            showSearchHandler,
            searchIsVisible,
            hideSearchHandler,
            searchType,
            submitSearchHandler,
            searchResultsAreVisible,
            openMobileNavHandler,
            closeMobileNavHandler,
            mobileNavIsOpen,
            logOutHandler,
            toggleFavoriteHandler,
            subMenuItems,
        } = this.props;
        let pageContent;

        if (searchResultsAreVisible) {
            pageContent = <SearchResults />
        } else {
            pageContent = (
                <LibraryContainer>
                    <LibraryList items={library} favoriteHandler={toggleFavoriteHandler} />
                    <FixedActionButton clickHandler={() => showSearchHandler(STANDARD_SEARCH_TYPE)} />
                </LibraryContainer>
            );
        }

        return (

                <Helmet>
                    <title>Dashboard | MMDb</title>
                    <meta name="description" content="My movie database" />
                </Helmet>

                <MobileNav
                    open={mobileNavIsOpen}
                    closeHandler={closeMobileNavHandler}
                    navItems={[
                        { display: 'Home', url: DASHBOARD_URL, icon: feather.icons.home },
                        { display: 'Logout', url: LOGIN_URL, onClickHandler: logOutHandler, icon: feather.icons['log-out'] },
                    ]}
                />
                 submitSearchHandler(query, searchType)}
                    openMobileNavHandler={openMobileNavHandler}
                />
                <SubMenu items={subMenuItems} />

                {pageContent}

        );
    }
}

const withConnect = connect(
    (state) => ({
        searchResultsAreVisible: state.search.resultsAreVisible,
        searchIsVisible: state.search.isVisible,
        searchType: state.search.searchType,
        library: state.dashboard.library,
        mobileNavIsOpen: state.dashboard.mobileNavIsOpen,
        subMenuItems: state.dashboard.subMenu,
    }),
    (dispatch, { location }) => ({
        onLoad: () => {
            Promise.all([
                dispatch(prepareMoviesForDashboard(location)),
                dispatch(prepareMovieGenres()),
            ]).then(() => dispatch(prepareRecentFormats()));
        },
        refreshDashboard: () => {
            dispatch(prepareMoviesForDashboard(location));
        },
        showSearchHandler: (type) => dispatch(showSearch(type)),
        hideSearchHandler: () => {
            dispatch(hideSearch());
            dispatch(prepareMoviesForDashboard());
        },
        submitSearchHandler: (query, searchType) => {
            dispatch(prepareSearchResults(query, searchType));
        },
        openMobileNavHandler: () => {
            dispatch(openMobileNav());
        },
        closeMobileNavHandler: () => {
            dispatch(closeMobileNav());
        },
        logOutHandler: () => {
            dispatch(closeMobileNav());
            dispatch(logOutUser());
        },
        toggleFavoriteHandler: (movie) => {
            const { isFavorite } = movie;

            if (isFavorite) {
                dispatch(unFavoriteMovie(movie))
            } else {
                dispatch(favoriteMovie(movie));
            }
        },
    })
);

const withReducer = injectReducer({ key: 'dashboard', reducer });

export default compose(
    withReducer,
    withConnect,
)(Dashboard);
<|endoftext|>


<commit_before>if (!Array.prototype.map) {
    Array.prototype.map = function (fun /*, thisp */) {
        "use strict";

        if (this === void 0 || this === null) {
            throw new TypeError();
        }

        var t = Object(this),
            // jshint bitwise: false
            len = t.length >>> 0;

        if (typeof fun !== 'function') {
            throw new TypeError();
        }

        var res = new Array(len),
            thisp = arguments[1];

        for (var i = 0; i < len; i++) {
            if (i in t) {
                res[i] = fun.call(thisp, t[i], i, t);
            }
        }

        return res;
    };
}
<commit_msg>Add helpers<commit_after>if (!Array.prototype.map) {
    // Add map() to array
    Array.prototype.map = function (fun /*, thisp */) {
        "use strict";

        if (this === void 0 || this === null) {
            throw new TypeError();
        }

        var t = Object(this),
            // jshint bitwise: false
            len = t.length >>> 0;

        if (typeof fun !== 'function') {
            throw new TypeError();
        }

        var res = new Array(len),
            thisp = arguments[1];

        for (var i = 0; i < len; i++) {
            if (i in t) {
                res[i] = fun.call(thisp, t[i], i, t);
            }
        }

        return res;
    };
}

if (!HTMLElement.prototype.click) {
    // Add click() to HTMLElement
    HTMLElement.prototype.click = function() {
        var ev = document.createEvent('MouseEvent');
        ev.initMouseEvent(
            'click',
            /*bubble*/true, /*cancelable*/true,
            window, null,
            0, 0, 0, 0, /*coordinates*/
            false, false, false, false, /*modifier keys*/
            0/*button=left*/, null
        );
        this.dispatchEvent(ev);
    };
}
<|endoftext|>


<commit_before>/*
 *
 * YoutubePlayer
 *
 */

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';

import YouTube from 'react-youtube';

import mapStateToProps from './selectors';
import mapDispatchToProps from './actions';

import YoutubeOverlay from 'components/YoutubeOverlay';

class YoutubePlayer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      width: window.innerWidth > 1800 ? 1280 : 853,
    };
  }

  render() {
    const {
      videoId,
      onReady,
      setRTCMute,
    } = this.props;
    const { width } = this.state;
    const height = width === 1280 ? 720 : 480;

    const containerStyle = {
      height,
      width,
      marginBottom: 10,
      position: 'relative',
    };

    const opts = {
      height,
      width,
      playerVars: {
        autoplay: 1,
      },
    };

    return (

        <YouTube
          videoId={videoId}
          opts={opts}
          onReady={e => onReady(e.target)}
          onPlay={setRTCMute(true)}
          onPause={setRTCMute(false)}
          onEnd={setRTCMute(false)}
          onError={setRTCMute(false)}
        />
        {videoId ? null : <YoutubeOverlay />}

    );
  }
}

YoutubePlayer.propTypes = {
  videoId: PropTypes.string.isRequired,
  onReady: PropTypes.func.isRequired,
  setRTCMute: PropTypes.func.isRequired,
};

export default connect(mapStateToProps, mapDispatchToProps)(YoutubePlayer);
<commit_msg>Use function component for youtube player container<commit_after>/*
 *
 * YoutubePlayer
 *
 */

import React, { PropTypes } from 'react';
import { connect } from 'react-redux';

import YouTube from 'react-youtube';

import mapStateToProps from './selectors';
import mapDispatchToProps from './actions';

import YoutubeOverlay from 'components/YoutubeOverlay';

const YoutubePlayer = ({ videoId, onReady, setRTCMute }) => {
  const [width, height] = window.innerWidth > 1800 ?
    [1280, 720] : [853, 480];

  const containerStyle = {
    height,
    width,
    marginBottom: 10,
    position: 'relative',
  };

  const opts = {
    height,
    width,
    playerVars: {
      autoplay: 1,
    },
  };

  return (

      <YouTube
        videoId={videoId}
        opts={opts}
        onReady={e => onReady(e.target)}
        onPlay={setRTCMute(true)}
        onPause={setRTCMute(false)}
        onEnd={setRTCMute(false)}
        onError={setRTCMute(false)}
      />
      {videoId ? null : <YoutubeOverlay />}

  );
};

YoutubePlayer.propTypes = {
  videoId: PropTypes.string.isRequired,
  onReady: PropTypes.func.isRequired,
  setRTCMute: PropTypes.func.isRequired,
};

export default connect(mapStateToProps, mapDispatchToProps)(YoutubePlayer);
<|endoftext|>


<commit_before>import React from 'react';

import SidebarMenuWrapper from '../../../lib/sidebar/SidebarMenuWrapper';
import SidebarMenuHeader from '../../../lib/sidebar/SidebarMenuHeader';
import TreeMenu from '../../../lib/sidebar/TreeMenu';

const mainMenus = [
  {
    key: 1,
    id: 1,
    icon: 'fa fa-dashboard',
    title: 'Dashboard',
    items: [
      { key: 11, id: 11, title: 'Dashboard v1', url: '/dashboard/v1' },
      { key: 12, id: 12, title: 'Dashboard v2', url: '/dashboard/v2' },
    ],
  },
  {
    key: 2,
    id: 2,
    icon: 'fa fa-files-o',
    title: 'Layout Options',
    label: '4',
    items: [
      { key: 21, id: 21, title: 'Top Navigation' },
      { key: 22, id: 22, title: 'Boxed' },
      { key: 23, id: 23, title: 'Fixed' },
      { key: 24, id: 24, title: 'Collapsed Sidebar' },
    ],
  },
  {
    key: 3,
    id: 3,
    icon: 'fa fa-th',
    title: 'Widgets',
    label: 'new',
    labelColor: 'green',
    url: '/widgets',
  },
  {
    key: 4,
    id: 4,
    icon: 'fa fa-pie-chart',
    title: 'Charts',
    items: [
      { key: 41, id: 41, title: 'ChartJS' },
      { key: 42, id: 42, title: 'Morris' },
      { key: 43, id: 43, title: 'Flot' },
      { key: 44, id: 44, title: 'Inline charts' },
    ],
  },
  {
    key: 5,
    id: 5,
    icon: 'fa fa-laptop',
    title: 'UI Elements',
    items: [
      { key: 51, id: 51, title: 'General' },
      { key: 52, id: 52, title: 'Icons' },
      { key: 53, id: 53, title: 'Buttons' },
      { key: 54, id: 54, title: 'Sliders' },
      { key: 55, id: 55, title: 'Timeline' },
      { key: 56, id: 56, title: 'Modals' },
    ],
  },
  {
    key: 6,
    id: 6,
    icon: 'fa fa-edit',
    title: 'Forms',
    items: [
      { key: 61, id: 61, title: 'General Elements' },
      { key: 62, id: 62, title: 'Advanced Elements' },
      { key: 63, id: 63, title: 'Editors' },
    ],
  },
  {
    key: 7,
    id: 7,
    icon: 'fa fa-table',
    title: 'Tables',
    items: [
      { key: 71, id: 71, title: 'Simple tables' },
      { key: 72, id: 72, title: 'Data tables' },
    ],
  },
  {
    key: 8,
    id: 8,
    icon: 'fa fa-calendar',
    title: 'Calendar',
    label: '3',
    labelColor: 'red',
  },
  {
    key: 9,
    id: 9,
    icon: 'fa fa-envelope',
    title: 'Mailbox',
    label: '12',
    labelColor: 'yellow',
    items: [
      {
        key: 91,
        id: 91,
        noIcon: true,
        title: 'Inbox',
        label: '13',
        labelColor: 'blue',
      },
      {
        key: 92,
        id: 92,
        noIcon: true,
        title: 'Compose',
      },
      {
        key: 93,
        id: 93,
        noIcon: true,
        title: 'Read',
      },
    ],
  },
  {
    key: 10,
    id: 10,
    icon: 'fa fa-folder',
    title: 'Examples',
    items: [
      { key: 101, id: 101, title: 'Invoice' },
      { key: 102, id: 102, title: 'Profile' },
      { key: 103, id: 103, title: 'Login' },
      { key: 104, id: 104, title: 'Register' },
      { key: 105, id: 105, title: 'Lockscreen' },
      { key: 106, id: 106, title: '404 Error' },
      { key: 107, id: 107, title: '500 Error' },
      { key: 108, id: 108, title: 'Blank Page', url: '/examples/blank-page' },
      { key: 109, id: 109, title: 'Pace Page' },
    ],
  },
  {
    key: 11,
    id: 11,
    icon: 'fa fa-share',
    title: 'Multilevel',
    items: [
      { key: 111, id: 111, title: 'Level One', url: '/level-one' },
      {
        key: 112,
        id: 112,
        title: 'Level One',
        items: [
          { key: 1121, id: 1121, title: 'Level Two', url: '/level-two' },
          { key: 1122, id: 1122,
            title: 'Level Two',
            items: [
              {
                key: 112201,
                id: 112201,
                title: 'Level Three',
                url: '/level-three',
              },
              {
                key: 112202,
                id: 112202,
                title: 'Level Three',
                url: '/level-three',
              },
            ],
          },
        ],
      },
      { key: 113, id: 113, title: 'Level One', url: '/level-one' },
    ],
  },
  {
    key: 12,
    id: 12,
    icon: 'fa fa-book',
    title: 'Documentation',
    url: '/documentation',
  },
];

function onClick(item) {
  // eslint-disable-next-line no-alert
  alert(`menu ${item.id} clicked`);
}

function onItemClick(item) {
  // eslint-disable-next-line no-alert
  alert(`sub menu ${item.id} clicked`);
}

export default function SidebarMenu() {
  return (
    <SidebarMenuWrapper>
      <SidebarMenuHeader title="MAIN MENU" />
      {mainMenus.map((menu) =>
        <TreeMenu
          {...menu}
          onClick={() => onClick(menu)}
          onItemClick={onItemClick}
        />
      )}
      <SidebarMenuHeader title="LABELS" />
    </SidebarMenuWrapper>
  );
}
<commit_msg>call browserHistory if there is url in menu item<commit_after>import React from 'react';
import { browserHistory } from 'react-router';

import SidebarMenuWrapper from '../../../lib/sidebar/SidebarMenuWrapper';
import SidebarMenuHeader from '../../../lib/sidebar/SidebarMenuHeader';
import TreeMenu from '../../../lib/sidebar/TreeMenu';

const mainMenus = [
  {
    key: 1,
    id: 1,
    icon: 'fa fa-dashboard',
    title: 'Dashboard',
    items: [
      { key: 11, id: 11, title: 'Dashboard v1', url: '/dashboard/v1' },
      { key: 12, id: 12, title: 'Dashboard v2', url: '/dashboard/v2' },
    ],
  },
  {
    key: 2,
    id: 2,
    icon: 'fa fa-files-o',
    title: 'Layout Options',
    label: '4',
    items: [
      { key: 21, id: 21, title: 'Top Navigation' },
      { key: 22, id: 22, title: 'Boxed' },
      { key: 23, id: 23, title: 'Fixed' },
      { key: 24, id: 24, title: 'Collapsed Sidebar' },
    ],
  },
  {
    key: 3,
    id: 3,
    icon: 'fa fa-th',
    title: 'Widgets',
    label: 'new',
    labelColor: 'green',
    url: '/widgets',
  },
  {
    key: 4,
    id: 4,
    icon: 'fa fa-pie-chart',
    title: 'Charts',
    items: [
      { key: 41, id: 41, title: 'ChartJS' },
      { key: 42, id: 42, title: 'Morris' },
      { key: 43, id: 43, title: 'Flot' },
      { key: 44, id: 44, title: 'Inline charts' },
    ],
  },
  {
    key: 5,
    id: 5,
    icon: 'fa fa-laptop',
    title: 'UI Elements',
    items: [
      { key: 51, id: 51, title: 'General' },
      { key: 52, id: 52, title: 'Icons' },
      { key: 53, id: 53, title: 'Buttons' },
      { key: 54, id: 54, title: 'Sliders' },
      { key: 55, id: 55, title: 'Timeline' },
      { key: 56, id: 56, title: 'Modals' },
    ],
  },
  {
    key: 6,
    id: 6,
    icon: 'fa fa-edit',
    title: 'Forms',
    items: [
      { key: 61, id: 61, title: 'General Elements' },
      { key: 62, id: 62, title: 'Advanced Elements' },
      { key: 63, id: 63, title: 'Editors' },
    ],
  },
  {
    key: 7,
    id: 7,
    icon: 'fa fa-table',
    title: 'Tables',
    items: [
      { key: 71, id: 71, title: 'Simple tables' },
      { key: 72, id: 72, title: 'Data tables' },
    ],
  },
  {
    key: 8,
    id: 8,
    icon: 'fa fa-calendar',
    title: 'Calendar',
    label: '3',
    labelColor: 'red',
  },
  {
    key: 9,
    id: 9,
    icon: 'fa fa-envelope',
    title: 'Mailbox',
    label: '12',
    labelColor: 'yellow',
    items: [
      {
        key: 91,
        id: 91,
        noIcon: true,
        title: 'Inbox',
        label: '13',
        labelColor: 'blue',
      },
      {
        key: 92,
        id: 92,
        noIcon: true,
        title: 'Compose',
      },
      {
        key: 93,
        id: 93,
        noIcon: true,
        title: 'Read',
      },
    ],
  },
  {
    key: 10,
    id: 10,
    icon: 'fa fa-folder',
    title: 'Examples',
    items: [
      { key: 101, id: 101, title: 'Invoice' },
      { key: 102, id: 102, title: 'Profile' },
      { key: 103, id: 103, title: 'Login' },
      { key: 104, id: 104, title: 'Register' },
      { key: 105, id: 105, title: 'Lockscreen' },
      { key: 106, id: 106, title: '404 Error' },
      { key: 107, id: 107, title: '500 Error' },
      { key: 108, id: 108, title: 'Blank Page', url: '/examples/blank-page' },
      { key: 109, id: 109, title: 'Pace Page' },
    ],
  },
  {
    key: 11,
    id: 11,
    icon: 'fa fa-share',
    title: 'Multilevel',
    items: [
      { key: 111, id: 111, title: 'Level One', url: '/level-one' },
      {
        key: 112,
        id: 112,
        title: 'Level One',
        items: [
          { key: 1121, id: 1121, title: 'Level Two', url: '/level-two' },
          { key: 1122, id: 1122,
            title: 'Level Two',
            items: [
              {
                key: 112201,
                id: 112201,
                title: 'Level Three',
                url: '/level-three',
              },
              {
                key: 112202,
                id: 112202,
                title: 'Level Three',
                url: '/level-three',
              },
            ],
          },
        ],
      },
      { key: 113, id: 113, title: 'Level One', url: '/level-one' },
    ],
  },
  {
    key: 12,
    id: 12,
    icon: 'fa fa-book',
    title: 'Documentation',
    url: '/documentation',
  },
];

function onClick(item) {
  if (item.url) {
    browserHistory.push(item.url);
  }
}

function onItemClick(item) {
  if (item.url) {
    browserHistory.push(item.url);
  }
}

export default function SidebarMenu() {
  return (
    <SidebarMenuWrapper>
      <SidebarMenuHeader title="MAIN NAVIGATION" />
      {mainMenus.map((menu) =>
        <TreeMenu
          {...menu}
          onClick={() => onClick(menu)}
          onItemClick={onItemClick}
        />
      )}
      <SidebarMenuHeader title="LABELS" />
    </SidebarMenuWrapper>
  );
}
<|endoftext|>


<commit_before>define(['js/core/Component', 'underscore'], function (Component, _) {

    var exceptionSizes = [120, 178],
        PRODUCT = "product",
        COMPOSITION = "composition";

    var ImageService = Component.inherit('sprd.data.ImageService', {

        defaults: {
            subDomainCount: 10,
            endPoint: '//image.spreadshirt.net/image-server/v1',
            gateway: '/image-server/v1',

            detectWebP: true,

            supportsWebP: false
        },

        ctor: function() {
            this.callBase();

            if (this.$.detectWebP) {
                var self = this;

                var image = new Image();
                image.onerror = function() {
                    self.set("supportsWebP", false);
                };
                image.onload = function() {
                    self.set("supportsWebP", image.width == 1);
                };

                image.src = 'data:image/webp;base64,UklGRiwAAABXRUJQVlA4ICAAAAAUAgCdASoBAAEAL/3+/3+CAB/AAAFzrNsAAP5QAAAAAA==';
            }
        },

        virtualProductImage: function(product, vpString, viewId, options) {
            return this.buildUrl([
                'products',
                vpString,
                'views',
                viewId
            ], ImageService.getImageSizeParameter(options), parseInt(product ? product.$.id : 0));
        },

        productImage: function (productId, viewId, appearanceId, type, options) {
            var params = ImageService.getImageSizeParameter(options);
            if (appearanceId) {
                params.appearanceId = appearanceId;
            }
            return this.buildUrl([
                type === PRODUCT ? "products" : "compositions",
                productId,
                "views",
                viewId
            ],
                params,
            parseInt(productId || 0) + parseInt(viewId || 0) + parseInt(appearanceId || 0));
        },

        productTypeImage: function (productTypeId, viewId, appearanceId, options) {
            return this.buildUrl(['productTypes', productTypeId, 'views', viewId, 'appearances', appearanceId],
                ImageService.getImageSizeParameter(options), parseInt(productTypeId || 0) + parseInt(viewId || 0) + parseInt(appearanceId || 0));
        },

        productTypeSizeImage: function (productTypeId, options) {
            return this.buildUrl(["productTypes", productTypeId, "variants", "size"], ImageService.getImageSizeParameter(options), productTypeId);
        },

        designImage: function (designId, options) {
            options = options || {};
            var parameter = ImageService.getImageSizeParameter(options) || {};

            var printColors = options.printColors;
            if (printColors) {
                for (var i = 0; i < printColors.length; i++) {
                    parameter["colors[" + i + "]"] = printColors[i];
                }
            }

            var cacheId = options.cacheId || (designId || "").replace(/^.*?(\d+).*/, "$1");
            return this.buildUrl(['designs', designId], parameter, cacheId);
        },

        appearanceImage: function (appearanceId, options) {
            return this.buildUrl(['appearances', appearanceId], ImageService.getImageSizeParameter(options), appearanceId);
        },

        printColorImage: function (printColorId, options) {
            return this.buildUrl(['printColors', printColorId], ImageService.getImageSizeParameter(options), printColorId);
        },

        emptyImage: function () {
            return "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
        },

        fontUrl: function (font, extension) {
            extension = extension || "woff" || "svg#font";
            return this.buildUrl(['fontFamilies', font.getFontFamily().$.id, 'fonts', font.$.id + "." + extension]);
        },

        buildUrl: function (url, parameter, cacheId) {
            var imgServer,
                subDomainCount = this.$.subDomainCount;

            if (!isNaN(parseInt(cacheId))) {
                // use the full qualified endpoint
                imgServer = this.$.endPoint;
                imgServer = imgServer.replace(/(\/\/image)\./, '$1' + (cacheId % subDomainCount) + ".");
            } else {
                imgServer = this.$.gateway;
            }

            url = url || [];
            url.unshift(imgServer);

            if (this.$.supportsWebP) {
                url.push(".webp");
            }

            return ImageService.buildUrl(url, parameter);
        }
    });

    ImageService.exceptionalSizes = [120, 178];

    ImageService.normalizeImageSize = function (size) {

        if (_.indexOf(exceptionSizes, size) === -1) {
            // normalize size
            size = Math.ceil(size / 50) * 50;
        }

        // keep range
        return Math.max(50, Math.min(1200, size));
    };

    ImageService.getImageSizeParameter = function (options) {

        options = options || {};

        var ret = {};

        if (options.width) {
            ret.width = ImageService.normalizeImageSize(options.width);
        }

        if (options.height) {
            ret.height = ImageService.normalizeImageSize(options.height);
        }

        return ret;
    };

    ImageService.buildUrl = function (url, parameter) {
        var queryString = ImageService.buildQueryString(parameter);
        return url.join('/') + (queryString ? '?' + queryString : '');
    };

    ImageService.buildQueryString = function (parameter) {
        var ret = [];

        for (var key in parameter) {
            if (parameter.hasOwnProperty(key)) {
                ret.push(key + '=' + encodeURIComponent(parameter[key]));
            }
        }

        if (ret.length) {
            return ret.join('&');
        }
    };

    ImageService.ProductImageType = {
        PRODUCT: PRODUCT,
        COMPOSITION: COMPOSITION
    };

    return ImageService;

});
<commit_msg>add webp as mediaType instead of /.webp<commit_after>define(['js/core/Component', 'underscore'], function (Component, _) {

    var exceptionSizes = [120, 178],
        PRODUCT = "product",
        COMPOSITION = "composition";

    var ImageService = Component.inherit('sprd.data.ImageService', {

        defaults: {
            subDomainCount: 10,
            endPoint: '//image.spreadshirt.net/image-server/v1',
            gateway: '/image-server/v1',

            detectWebP: true,

            supportsWebP: false
        },

        ctor: function() {
            this.callBase();

            if (this.$.detectWebP) {
                var self = this;

                var image = new Image();
                image.onerror = function() {
                    self.set("supportsWebP", false);
                };
                image.onload = function() {
                    self.set("supportsWebP", image.width == 1);
                };

                image.src = 'data:image/webp;base64,UklGRiwAAABXRUJQVlA4ICAAAAAUAgCdASoBAAEAL/3+/3+CAB/AAAFzrNsAAP5QAAAAAA==';
            }
        },

        virtualProductImage: function(product, vpString, viewId, options) {
            return this.buildUrl([
                'products',
                vpString,
                'views',
                viewId
            ], ImageService.getImageSizeParameter(options), parseInt(product ? product.$.id : 0));
        },

        productImage: function (productId, viewId, appearanceId, type, options) {
            var params = ImageService.getImageSizeParameter(options);
            if (appearanceId) {
                params.appearanceId = appearanceId;
            }
            return this.buildUrl([
                type === PRODUCT ? "products" : "compositions",
                productId,
                "views",
                viewId
            ],
                params,
            parseInt(productId || 0) + parseInt(viewId || 0) + parseInt(appearanceId || 0));
        },

        productTypeImage: function (productTypeId, viewId, appearanceId, options) {
            return this.buildUrl(['productTypes', productTypeId, 'views', viewId, 'appearances', appearanceId],
                ImageService.getImageSizeParameter(options), parseInt(productTypeId || 0) + parseInt(viewId || 0) + parseInt(appearanceId || 0));
        },

        productTypeSizeImage: function (productTypeId, options) {
            return this.buildUrl(["productTypes", productTypeId, "variants", "size"], ImageService.getImageSizeParameter(options), productTypeId);
        },

        designImage: function (designId, options) {
            options = options || {};
            var parameter = ImageService.getImageSizeParameter(options) || {};

            var printColors = options.printColors;
            if (printColors) {
                for (var i = 0; i < printColors.length; i++) {
                    parameter["colors[" + i + "]"] = printColors[i];
                }
            }

            var cacheId = options.cacheId || (designId || "").replace(/^.*?(\d+).*/, "$1");
            return this.buildUrl(['designs', designId], parameter, cacheId);
        },

        appearanceImage: function (appearanceId, options) {
            return this.buildUrl(['appearances', appearanceId], ImageService.getImageSizeParameter(options), appearanceId);
        },

        printColorImage: function (printColorId, options) {
            return this.buildUrl(['printColors', printColorId], ImageService.getImageSizeParameter(options), printColorId);
        },

        emptyImage: function () {
            return "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
        },

        fontUrl: function (font, extension) {
            extension = extension || "woff" || "svg#font";
            return this.buildUrl(['fontFamilies', font.getFontFamily().$.id, 'fonts', font.$.id + "." + extension]);
        },

        buildUrl: function (url, parameter, cacheId) {
            var imgServer,
                subDomainCount = this.$.subDomainCount;

            if (!isNaN(parseInt(cacheId))) {
                // use the full qualified endpoint
                imgServer = this.$.endPoint;
                imgServer = imgServer.replace(/(\/\/image)\./, '$1' + (cacheId % subDomainCount) + ".");
            } else {
                imgServer = this.$.gateway;
            }

            url = url || [];
            url.unshift(imgServer);

            if (this.$.supportsWebP) {
                parameter = parameter || {};
                parameter.mediaType = parameter.mediaType || "webp";
            }

            return ImageService.buildUrl(url, parameter);
        }
    });

    ImageService.exceptionalSizes = [120, 178];

    ImageService.normalizeImageSize = function (size) {

        if (_.indexOf(exceptionSizes, size) === -1) {
            // normalize size
            size = Math.ceil(size / 50) * 50;
        }

        // keep range
        return Math.max(50, Math.min(1200, size));
    };

    ImageService.getImageSizeParameter = function (options) {

        options = options || {};

        var ret = {};

        if (options.width) {
            ret.width = ImageService.normalizeImageSize(options.width);
        }

        if (options.height) {
            ret.height = ImageService.normalizeImageSize(options.height);
        }

        return ret;
    };

    ImageService.buildUrl = function (url, parameter) {
        var queryString = ImageService.buildQueryString(parameter);
        return url.join('/') + (queryString ? '?' + queryString : '');
    };

    ImageService.buildQueryString = function (parameter) {
        var ret = [];

        for (var key in parameter) {
            if (parameter.hasOwnProperty(key)) {
                ret.push(key + '=' + encodeURIComponent(parameter[key]));
            }
        }

        if (ret.length) {
            return ret.join('&');
        }
    };

    ImageService.ProductImageType = {
        PRODUCT: PRODUCT,
        COMPOSITION: COMPOSITION
    };

    return ImageService;

});
<|endoftext|>


<commit_before>/* NOTE: This file is processed by server/state.js
         and served by server/server.js (to auth'd users only) */

yepnope(mediaURL + 'css/mod-v1.css');

var $selectButton, $controls;
window.loggedInUser = IDENT.email;
window.x_csrf = IDENT.csrf;

function show_toolbox() {
	var specs = [
		{name: 'Lewd', kind: 7},
		{name: 'Porn', kind: 8},
		{name: 'Delete', kind: 9},
		{name: 'Lock', kind: 11},
	];
	if (IDENT.auth == 'Admin')
		specs.push({name: 'Panel', kind: 'panel'});
	var $toolbox = $('
', {id: 'toolbox', "class": 'mod'});

	$selectButton = $('<input />', {
		type: 'button', val: 'Select',
		click: function (e) { toggle_multi_selecting(); },
	});
	$toolbox.append($selectButton, ' ');

	$controls = $('').hide();
	_.each(specs, function (spec) {
		$controls.append($('<input />', {
			type: 'button',
			val: spec.name,
			data: {kind: spec.kind},
		}), ' ');
	});
	$controls.on('click', 'input[type=button]', tool_action);

	_.each(delayNames, function (when, i) {
		var id = 'delay-' + when;
		var $label = $('', {text: when, 'for': id});
		var $radio = $('<input/>', {
			id: id, type: 'radio', val: when, name: 'delay',
		});
		if (i == 0)
			$radio.prop('checked', true);
		$controls.append($radio, $label, ' ');
	});

	$toolbox.append($controls).insertBefore(THREAD ? 'hr:last' : $ceiling);
}

function tool_action(event) {
	var ids = [];
	var $sel = $('.selected');
	$sel.each(function () {
		var id = extract_num(parent_post($(this)));
		if (id)
			ids.push(id);
	});
	var $button = $(this);
	var kind = $button.data('kind');
	if (kind == 'panel')
		return toggle_panel();

	/* On a thread page there's only one thread to lock, so... */
	if (kind == 11 && THREAD && !ids.length)
		ids = [THREAD];

	if (ids.length) {
		var when = $('input:radio[name=delay]:checked').val();
		ids.unshift(parseInt(kind, 10), {when: when});
		send(ids);
		$sel.removeClass('selected');
	}
	else {
		var orig = $button.val();
		var caption = _.bind($button.val, $button);
		caption('Nope.');
		if (orig != 'Nope.')
			_.delay(caption, 2000, orig);
	}
}

readOnly.push('graveyard');
menuOptions.unshift('Select');

var multiSelecting = false;

function toggle_multi_selecting($post) {
	var oldTarget;
	if ($post) {
		oldTarget = lockTarget;
		set_lock_target(extract_num($post));
	}
	with_dom(function () {
	multiSelecting = !multiSelecting;
	if (multiSelecting) {
		$('body').addClass('multi-select');
		make_selection_handle().prependTo('article');
		make_selection_handle().prependTo('section > header');
		if ($post)
			select_post($post);
		$controls.show();
		$selectButton.val('X');
	}
	else {
		$('body').removeClass('multi-select');
		$('.select-handle').remove();
		$controls.hide();
		$selectButton.val('Select');
	}
	});
	if ($post)
		set_lock_target(oldTarget);
}

menuHandlers.Select = toggle_multi_selecting;

function enable_multi_selecting() {
	if (!multiSelecting)
		toggle_multi_selecting();
}

function select_post($post) {
	$post.find('.select-handle:first').addClass('selected');
}

function make_selection_handle() {
	return $('');
}

window.fun = function () {
	send([33, THREAD]);
};

override(ComposerView.prototype, 'make_alloc_request',
			function (orig, text, img) {
	var msg = orig.call(this, text, img);
	if ($('#authname').is(':checked'))
		msg.auth = IDENT.auth;
	return msg;
});

$DOC.on('click', '.select-handle', function (event) {
	event.preventDefault();
	$(event.target).toggleClass('selected');
});

with_dom(function () {
	$('h1').text('Moderation - ' + $('h1').text());
	var $authname = $('<input>', {type: 'checkbox', id: 'authname'});
	var $label = $('', {text: ' '+IDENT.auth}).prepend($authname);
	$name.after(' ', $label);

	/* really ought to be done with model observation! */
	$authname.change(function () {
		if (postForm)
			postForm.propagate_ident();
	});
	oneeSama.hook('fillMyName', function ($el) {
		var auth = $('#authname').is(':checked');
		$el.toggleClass(IDENT.auth.toLowerCase(), auth);
		if (auth)
			$el.append(' ## ' + IDENT.auth)
	});

	oneeSama.hook('afterInsert', function (target) {
		if (multiSelecting)
			make_selection_handle().prependTo(target);
	});
	show_toolbox();
});

var Address = Backbone.Model.extend({
	idAttribute: 'ip',
	defaults: {
		count: 0,
	},
});

var AddressView = Backbone.View.extend({
	className: 'mod address',
	events: {
		'keydown .name': 'entered_name',
		'click .sel-all': 'select_all',
	},

	initialize: function () {
		var $el = this.$el;
		$('', {"class": 'ip'}).appendTo($el);
		if (CurThread) {
			$el.append('   ', $('<input/>', {
				"class": 'sel-all',
				type: 'button',
				val: 'Sel All'
			}));
		}
		$el.append(
			'
',
			$('<input>', {"class": 'name', placeholder: 'Name'})
		);
		this.listenTo(this.model, 'change', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		if (attrs.shallow) {
			this.$('.ip').text('Loading...');
			return this;
		}
		this.$('.ip').text(attrs.ip);
		var $name = this.$('.name');
		if (!this.focusedName) {
			_.defer(function () {
				$name.focus().prop({
					selectionStart: 0,
					selectionEnd: $name.val().length,
				});
			});
			this.focusedName = true;
		}
		if (attrs.name != $name.val()) {
			$name.val(attrs.name);
		}
		return this;
	},

	entered_name: function (event) {
		if (event.which != 13)
			return;
		event.preventDefault();
		var name = this.$('.name').val().trim();
		send([102, this.model.id, name]);
		this.remove();
	},

	remove: function () {
		this.trigger('preremove');
		Backbone.View.prototype.remove.call(this);
	},

	select_all: function () {
		var models = CurThread.where({ip: this.model.get('ip')});
		if (!models.length)
			return;
		enable_multi_selecting();
		with_dom(function () {
			$.each(models, function () {
				select_post($('#' + this.id));
			});
		});
	},
});

// basically just a link
var AddrView = Backbone.View.extend({
	tagName: 'a',
	className: 'mod addr',

	events: {
		click: 'toggle_expansion',
	},

	initialize: function () {
		this.$el.attr('href', '#');
		this.listenTo(this.model, 'change:name', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		var text = ip_mnemonic(attrs.ip);
		if (attrs.name)
			text += ' "' + attrs.name + '"';
		this.$el.attr('title', attrs.ip).text(text);
		return this;
	},

	toggle_expansion: function (event) {
		if (event.target !== this.el)
			return;
		event.preventDefault();

		if (this.expansion)
			return this.expansion.remove();

		this.expansion = new AddressView({model: this.model});
		this.$el.after(this.expansion.render().el);
		this.listenTo(this.expansion, 'preremove',
				this.expansion_removed);

		if (this.model.get('shallow'))
			send([101, this.model.id]);
	},

	remove: function () {
		if (this.expansion)
			this.expansion.remove();
		Backbone.View.prototype.remove.call(this);
	},

	expansion_removed: function () {
		this.expansion = null;
	},
});

var Addresses = Backbone.Collection.extend({
	model: Address,
	comparator: function (a) { return ip_mnemonic(a.ip); },
});

window.addrs = new Addresses;

function hook_up_address($post) {
	var $a = $post.find('a.mod.addr');
	if (!$a.length)
		return;
	var ip = $a.prop('title') || $a.text();
	var givenName;
	var m = $a.text().match(/^([\w'.]+) "(.+)"$/);
	if (m) {
		if (is_valid_ip(m[1]))
			ip = m[1];
		givenName = m[2];
	}
	if (!is_valid_ip(ip))
		return;

	/* Activate this address link */
	var address = window.addrs.get(ip);
	if (!address) {
		address = new Address({ip: ip});
		address.set(givenName ? {name: givenName} : {shallow: true});
		window.addrs.add(address);
	}
	var view = new AddrView({model: address, el: $a[0]});
	if (address.get('name'))
		view.render();

	/* Augment post with IP */
	if (CurThread) {
		var post = CurThread.get($post.attr('id'));
		if (post && !post.has('ip'))
			post.set('ip', ip);
	}
}
oneeSama.hook('afterInsert', hook_up_address);

with_dom(function () {
	$('article, section').each(function () {
		hook_up_address($(this));
	});
});

window.adminState = new Backbone.Model({
});

var PanelView = Backbone.View.extend({
	id: 'panel',

	initialize: function () {
		this.listenTo(this.model, 'change:visible', this.renderVis);
		this.listenTo(window.addrs, 'add change:count reset',
				this.renderIPs);
		this.listenTo(this.model, 'change:memoryUsage',
				this.renderMemory);
		this.listenTo(this.model, 'change:addrs change:bans',
				this.renderCounts);
		this.listenTo(this.model, 'change:uptime', this.renderUptime);
		$('
', {id: 'ips'}).appendTo(this.el);
		$('
', {id: 'mem'}).appendTo(this.el);
		$('
', {id: 'counts'}).appendTo(this.el);
		$('
', {id: 'uptime'}).appendTo(this.el);
	},

	renderVis: function (model, vis) {
		this.$el.toggle(!!vis);
	},

	renderIPs: function () {
		var $ips = this.$('#ips').empty();
		window.addrs.forEach(function (addr) {
			var n = addr.get('count');
			if (!n)
				return;
			var el = new AddrView({model: addr}).render().el;
			$ips.append(el, n>1 ? ' ('+n+')' : '', '
');
		});
	},

	renderMemory: function (model, mem) {
		function mb(n) {
			return Math.round(n/1000000);
		}
		this.$('#mem').text(
			mb(mem.heapUsed) + '/' + mb(mem.heapTotal) +
			' MB heap used.'
		);
	},

	renderCounts: function (model) {
		var a = model.attributes;
		this.$('#counts').text(pluralize(a.addrs, 'addr') + ', ' +
				pluralize(a.bans, 'ban') + '.');
	},

	renderUptime: function (model, s) {
		var m = Math.floor(s / 60) % 60;
		var h = Math.floor(s / 3600) % 60;
		var d = Math.floor(s / (3600*24));
		h = h ? h+'h' : '';
		d = d ? d+'d' : '';
		this.$('#uptime').text('Up '+ d + h + m +'m.');
	},
});

function toggle_panel() {
	var show = !adminState.get('visible');
	send([show ? 60 : 61, 'adminState']);
}

if (IDENT.auth == 'Admin') (function () {
	var $panel = $('
', {id: 'panel', "class": 'mod'}).hide();
	var view = new PanelView({model: adminState, el: $panel[0]});
	$panel.appendTo('body');
})();
<commit_msg>Remove popup after Sel All<commit_after>/* NOTE: This file is processed by server/state.js
         and served by server/server.js (to auth'd users only) */

yepnope(mediaURL + 'css/mod-v1.css');

var $selectButton, $controls;
window.loggedInUser = IDENT.email;
window.x_csrf = IDENT.csrf;

function show_toolbox() {
	var specs = [
		{name: 'Lewd', kind: 7},
		{name: 'Porn', kind: 8},
		{name: 'Delete', kind: 9},
		{name: 'Lock', kind: 11},
	];
	if (IDENT.auth == 'Admin')
		specs.push({name: 'Panel', kind: 'panel'});
	var $toolbox = $('
', {id: 'toolbox', "class": 'mod'});

	$selectButton = $('<input />', {
		type: 'button', val: 'Select',
		click: function (e) { toggle_multi_selecting(); },
	});
	$toolbox.append($selectButton, ' ');

	$controls = $('').hide();
	_.each(specs, function (spec) {
		$controls.append($('<input />', {
			type: 'button',
			val: spec.name,
			data: {kind: spec.kind},
		}), ' ');
	});
	$controls.on('click', 'input[type=button]', tool_action);

	_.each(delayNames, function (when, i) {
		var id = 'delay-' + when;
		var $label = $('', {text: when, 'for': id});
		var $radio = $('<input/>', {
			id: id, type: 'radio', val: when, name: 'delay',
		});
		if (i == 0)
			$radio.prop('checked', true);
		$controls.append($radio, $label, ' ');
	});

	$toolbox.append($controls).insertBefore(THREAD ? 'hr:last' : $ceiling);
}

function tool_action(event) {
	var ids = [];
	var $sel = $('.selected');
	$sel.each(function () {
		var id = extract_num(parent_post($(this)));
		if (id)
			ids.push(id);
	});
	var $button = $(this);
	var kind = $button.data('kind');
	if (kind == 'panel')
		return toggle_panel();

	/* On a thread page there's only one thread to lock, so... */
	if (kind == 11 && THREAD && !ids.length)
		ids = [THREAD];

	if (ids.length) {
		var when = $('input:radio[name=delay]:checked').val();
		ids.unshift(parseInt(kind, 10), {when: when});
		send(ids);
		$sel.removeClass('selected');
	}
	else {
		var orig = $button.val();
		var caption = _.bind($button.val, $button);
		caption('Nope.');
		if (orig != 'Nope.')
			_.delay(caption, 2000, orig);
	}
}

readOnly.push('graveyard');
menuOptions.unshift('Select');

var multiSelecting = false;

function toggle_multi_selecting($post) {
	var oldTarget;
	if ($post) {
		oldTarget = lockTarget;
		set_lock_target(extract_num($post));
	}
	with_dom(function () {
	multiSelecting = !multiSelecting;
	if (multiSelecting) {
		$('body').addClass('multi-select');
		make_selection_handle().prependTo('article');
		make_selection_handle().prependTo('section > header');
		if ($post)
			select_post($post);
		$controls.show();
		$selectButton.val('X');
	}
	else {
		$('body').removeClass('multi-select');
		$('.select-handle').remove();
		$controls.hide();
		$selectButton.val('Select');
	}
	});
	if ($post)
		set_lock_target(oldTarget);
}

menuHandlers.Select = toggle_multi_selecting;

function enable_multi_selecting() {
	if (!multiSelecting)
		toggle_multi_selecting();
}

function select_post($post) {
	$post.find('.select-handle:first').addClass('selected');
}

function make_selection_handle() {
	return $('');
}

window.fun = function () {
	send([33, THREAD]);
};

override(ComposerView.prototype, 'make_alloc_request',
			function (orig, text, img) {
	var msg = orig.call(this, text, img);
	if ($('#authname').is(':checked'))
		msg.auth = IDENT.auth;
	return msg;
});

$DOC.on('click', '.select-handle', function (event) {
	event.preventDefault();
	$(event.target).toggleClass('selected');
});

with_dom(function () {
	$('h1').text('Moderation - ' + $('h1').text());
	var $authname = $('<input>', {type: 'checkbox', id: 'authname'});
	var $label = $('', {text: ' '+IDENT.auth}).prepend($authname);
	$name.after(' ', $label);

	/* really ought to be done with model observation! */
	$authname.change(function () {
		if (postForm)
			postForm.propagate_ident();
	});
	oneeSama.hook('fillMyName', function ($el) {
		var auth = $('#authname').is(':checked');
		$el.toggleClass(IDENT.auth.toLowerCase(), auth);
		if (auth)
			$el.append(' ## ' + IDENT.auth)
	});

	oneeSama.hook('afterInsert', function (target) {
		if (multiSelecting)
			make_selection_handle().prependTo(target);
	});
	show_toolbox();
});

var Address = Backbone.Model.extend({
	idAttribute: 'ip',
	defaults: {
		count: 0,
	},
});

var AddressView = Backbone.View.extend({
	className: 'mod address',
	events: {
		'keydown .name': 'entered_name',
		'click .sel-all': 'select_all',
	},

	initialize: function () {
		var $el = this.$el;
		$('', {"class": 'ip'}).appendTo($el);
		if (CurThread) {
			$el.append('   ', $('<input/>', {
				"class": 'sel-all',
				type: 'button',
				val: 'Sel All'
			}));
		}
		$el.append(
			'
',
			$('<input>', {"class": 'name', placeholder: 'Name'})
		);
		this.listenTo(this.model, 'change', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		if (attrs.shallow) {
			this.$('.ip').text('Loading...');
			return this;
		}
		this.$('.ip').text(attrs.ip);
		var $name = this.$('.name');
		if (!this.focusedName) {
			_.defer(function () {
				$name.focus().prop({
					selectionStart: 0,
					selectionEnd: $name.val().length,
				});
			});
			this.focusedName = true;
		}
		if (attrs.name != $name.val()) {
			$name.val(attrs.name);
		}
		return this;
	},

	entered_name: function (event) {
		if (event.which != 13)
			return;
		event.preventDefault();
		var name = this.$('.name').val().trim();
		send([102, this.model.id, name]);
		this.remove();
	},

	remove: function () {
		this.trigger('preremove');
		Backbone.View.prototype.remove.call(this);
	},

	select_all: function () {
		var models = CurThread.where({ip: this.model.get('ip')});
		if (!models.length)
			return;
		enable_multi_selecting();
		with_dom(function () {
			$.each(models, function () {
				select_post($('#' + this.id));
			});
		});
		this.remove();
	},
});

// basically just a link
var AddrView = Backbone.View.extend({
	tagName: 'a',
	className: 'mod addr',

	events: {
		click: 'toggle_expansion',
	},

	initialize: function () {
		this.$el.attr('href', '#');
		this.listenTo(this.model, 'change:name', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		var text = ip_mnemonic(attrs.ip);
		if (attrs.name)
			text += ' "' + attrs.name + '"';
		this.$el.attr('title', attrs.ip).text(text);
		return this;
	},

	toggle_expansion: function (event) {
		if (event.target !== this.el)
			return;
		event.preventDefault();

		if (this.expansion)
			return this.expansion.remove();

		this.expansion = new AddressView({model: this.model});
		this.$el.after(this.expansion.render().el);
		this.listenTo(this.expansion, 'preremove',
				this.expansion_removed);

		if (this.model.get('shallow'))
			send([101, this.model.id]);
	},

	remove: function () {
		if (this.expansion)
			this.expansion.remove();
		Backbone.View.prototype.remove.call(this);
	},

	expansion_removed: function () {
		this.expansion = null;
	},
});

var Addresses = Backbone.Collection.extend({
	model: Address,
	comparator: function (a) { return ip_mnemonic(a.ip); },
});

window.addrs = new Addresses;

function hook_up_address($post) {
	var $a = $post.find('a.mod.addr');
	if (!$a.length)
		return;
	var ip = $a.prop('title') || $a.text();
	var givenName;
	var m = $a.text().match(/^([\w'.]+) "(.+)"$/);
	if (m) {
		if (is_valid_ip(m[1]))
			ip = m[1];
		givenName = m[2];
	}
	if (!is_valid_ip(ip))
		return;

	/* Activate this address link */
	var address = window.addrs.get(ip);
	if (!address) {
		address = new Address({ip: ip});
		address.set(givenName ? {name: givenName} : {shallow: true});
		window.addrs.add(address);
	}
	var view = new AddrView({model: address, el: $a[0]});
	if (address.get('name'))
		view.render();

	/* Augment post with IP */
	if (CurThread) {
		var post = CurThread.get($post.attr('id'));
		if (post && !post.has('ip'))
			post.set('ip', ip);
	}
}
oneeSama.hook('afterInsert', hook_up_address);

with_dom(function () {
	$('article, section').each(function () {
		hook_up_address($(this));
	});
});

window.adminState = new Backbone.Model({
});

var PanelView = Backbone.View.extend({
	id: 'panel',

	initialize: function () {
		this.listenTo(this.model, 'change:visible', this.renderVis);
		this.listenTo(window.addrs, 'add change:count reset',
				this.renderIPs);
		this.listenTo(this.model, 'change:memoryUsage',
				this.renderMemory);
		this.listenTo(this.model, 'change:addrs change:bans',
				this.renderCounts);
		this.listenTo(this.model, 'change:uptime', this.renderUptime);
		$('
', {id: 'ips'}).appendTo(this.el);
		$('
', {id: 'mem'}).appendTo(this.el);
		$('
', {id: 'counts'}).appendTo(this.el);
		$('
', {id: 'uptime'}).appendTo(this.el);
	},

	renderVis: function (model, vis) {
		this.$el.toggle(!!vis);
	},

	renderIPs: function () {
		var $ips = this.$('#ips').empty();
		window.addrs.forEach(function (addr) {
			var n = addr.get('count');
			if (!n)
				return;
			var el = new AddrView({model: addr}).render().el;
			$ips.append(el, n>1 ? ' ('+n+')' : '', '
');
		});
	},

	renderMemory: function (model, mem) {
		function mb(n) {
			return Math.round(n/1000000);
		}
		this.$('#mem').text(
			mb(mem.heapUsed) + '/' + mb(mem.heapTotal) +
			' MB heap used.'
		);
	},

	renderCounts: function (model) {
		var a = model.attributes;
		this.$('#counts').text(pluralize(a.addrs, 'addr') + ', ' +
				pluralize(a.bans, 'ban') + '.');
	},

	renderUptime: function (model, s) {
		var m = Math.floor(s / 60) % 60;
		var h = Math.floor(s / 3600) % 60;
		var d = Math.floor(s / (3600*24));
		h = h ? h+'h' : '';
		d = d ? d+'d' : '';
		this.$('#uptime').text('Up '+ d + h + m +'m.');
	},
});

function toggle_panel() {
	var show = !adminState.get('visible');
	send([show ? 60 : 61, 'adminState']);
}

if (IDENT.auth == 'Admin') (function () {
	var $panel = $('
', {id: 'panel', "class": 'mod'}).hide();
	var view = new PanelView({model: adminState, el: $panel[0]});
	$panel.appendTo('body');
})();
<|endoftext|>


<commit_before>import { createElement } from 'react'
import ReactDOM from 'react-dom'
import HeadManager from './head-manager'
import { createRouter } from '../lib/router'
import EventEmitter from '../lib/EventEmitter'
import App from '../lib/app'
import { loadGetInitialProps, getURL } from '../lib/utils'
import PageLoader from '../lib/page-loader'

// Polyfill Promise globally
// This is needed because Webpack2's dynamic loading(common chunks) code
// depends on Promise.
// So, we need to polyfill it.
// See: https://github.com/webpack/webpack/issues/4254
if (!window.Promise) {
  window.Promise = Promise
}

const {
  __NEXT_DATA__: {
    props,
    err,
    pathname,
    query,
    buildId,
    assetPrefix
  },
  location
} = window

if (assetPrefix) {
  __webpack_public_path__ = `${assetPrefix}/_next/${buildId}/`    // eslint-disable-line
}

const asPath = getURL()

const pageLoader = new PageLoader(buildId, assetPrefix)
window.__NEXT_LOADED_PAGES__.forEach(({ route, fn }) => {
  pageLoader.registerPage(route, fn)
})
delete window.__NEXT_LOADED_PAGES__
window.__NEXT_REGISTER_PAGE = pageLoader.registerPage.bind(pageLoader)

const headManager = new HeadManager()
const appContainer = document.getElementById('__next')
const errorContainer = document.getElementById('__next-error')

let lastAppProps
export let router
export let ErrorComponent
let Component

export const emitter = new EventEmitter()

export default async () => {
  ErrorComponent = await pageLoader.loadPage('/_error')

  try {
    Component = await pageLoader.loadPage(pathname)
  } catch (err) {
    console.error(err)
    Component = ErrorComponent
  }

  router = createRouter(pathname, query, asPath, {
    pageLoader,
    Component,
    ErrorComponent,
    err
  })

  router.subscribe(({ Component, props, hash, err }) => {
    render({ Component, props, err, hash, emitter })
  })

  const hash = location.hash.substring(1)
  render({ Component, props, hash, err, emitter })

  return emitter
}

export async function render (props) {
  // There are some errors we should ignore.
  // Next.js rendering logic knows how to handle them.
  // These are specially 404 errors
  if (props.err && !props.err.ignore) {
    await renderError(props.err)
    return
  }

  try {
    await doRender(props)
  } catch (err) {
    if (err.abort) return
    await renderError(err)
  }
}

// This method handles all runtime and debug errors.
// 404 and 500 errors are special kind of errors
// and they are still handle via the main render method.
export async function renderError (error) {
  // We need to unmount the current app component because it's
  // in the inconsistant state.
  // Otherwise, we need to face issues when the issue is fixed and
  // it's get notified via HMR
  ReactDOM.unmountComponentAtNode(appContainer)

  console.error(error)

  const initProps = { err: error, pathname, query, asPath }
  const props = await loadGetInitialProps(ErrorComponent, initProps)
  renderReactElement(createElement(ErrorComponent, props), errorContainer)

  appContainer.innerHTML = ''
}

async function doRender ({ Component, props, hash, err, emitter: emitterProp = emitter }) {
  if (!props && Component &&
    Component !== ErrorComponent &&
    lastAppProps.Component === ErrorComponent) {
    // fetch props if ErrorComponent was replaced with a page component by HMR
    const { pathname, query, asPath } = router
    props = await loadGetInitialProps(Component, { err, pathname, query, asPath })
  }

  Component = Component || lastAppProps.Component
  props = props || lastAppProps.props

  const appProps = { Component, props, hash, err, router, headManager }
  // lastAppProps has to be set before ReactDom.render to account for ReactDom throwing an error.
  lastAppProps = appProps

  emitterProp.emit('before-reactdom-render', { Component, ErrorComponent, appProps })

  // We need to clear any existing runtime error messages
  ReactDOM.unmountComponentAtNode(errorContainer)
  errorContainer.innerHTML = ''

  renderReactElement(createElement(App, appProps), appContainer)

  emitterProp.emit('after-reactdom-render', { Component, ErrorComponent, appProps })
}

let isInitialRender = true
function renderReactElement (reactEl, domEl) {
  if (isInitialRender) {
    ReactDOM.hydrate(reactEl, domEl)
    isInitialRender = false
  } else {
    ReactDOM.render(reactEl, domEl)
  }
}
<commit_msg>Inline doRender method<commit_after>import { createElement } from 'react'
import ReactDOM from 'react-dom'
import HeadManager from './head-manager'
import { createRouter } from '../lib/router'
import EventEmitter from '../lib/EventEmitter'
import App from '../lib/app'
import { loadGetInitialProps, getURL } from '../lib/utils'
import PageLoader from '../lib/page-loader'

// Polyfill Promise globally
// This is needed because Webpack2's dynamic loading(common chunks) code
// depends on Promise.
// So, we need to polyfill it.
// See: https://github.com/webpack/webpack/issues/4254
if (!window.Promise) {
  window.Promise = Promise
}

const {
  __NEXT_DATA__: {
    props,
    err,
    pathname,
    query,
    buildId,
    assetPrefix
  },
  location
} = window

if (assetPrefix) {
  __webpack_public_path__ = `${assetPrefix}/_next/${buildId}/`    // eslint-disable-line
}

const asPath = getURL()

const pageLoader = new PageLoader(buildId, assetPrefix)
window.__NEXT_LOADED_PAGES__.forEach(({ route, fn }) => {
  pageLoader.registerPage(route, fn)
})
delete window.__NEXT_LOADED_PAGES__
window.__NEXT_REGISTER_PAGE = pageLoader.registerPage.bind(pageLoader)

const headManager = new HeadManager()
const appContainer = document.getElementById('__next')
const errorContainer = document.getElementById('__next-error')

let lastAppProps
export let router
export let ErrorComponent
let Component

export const emitter = new EventEmitter()

export default async () => {
  ErrorComponent = await pageLoader.loadPage('/_error')

  try {
    Component = await pageLoader.loadPage(pathname)
  } catch (err) {
    console.error(err)
    Component = ErrorComponent
  }

  router = createRouter(pathname, query, asPath, {
    pageLoader,
    Component,
    ErrorComponent,
    err
  })

  router.subscribe(({ Component, props, hash, err }) => {
    render({ Component, props, err, hash, emitter })
  })

  const hash = location.hash.substring(1)
  render({ Component, props, hash, err, emitter })

  return emitter
}

export async function render ({ Component, props, hash, err, emitter: emitterProp = emitter }) {
  // There are some errors we should ignore.
  // Next.js rendering logic knows how to handle them.
  // These are specially 404 errors
  if (err && !err.ignore) {
    await renderError(err)
    return
  }

  try {
    if (!props && Component &&
    Component !== ErrorComponent &&
    lastAppProps.Component === ErrorComponent) {
    // fetch props if ErrorComponent was replaced with a page component by HMR
      const { pathname, query, asPath } = router
      props = await loadGetInitialProps(Component, { err, pathname, query, asPath })
    }

    Component = Component || lastAppProps.Component
    props = props || lastAppProps.props

    const appProps = { Component, props, hash, err, router, headManager }
    // lastAppProps has to be set before ReactDom.render to account for ReactDom throwing an error.
    lastAppProps = appProps

    emitterProp.emit('before-reactdom-render', { Component, ErrorComponent, appProps })

    // We need to clear any existing runtime error messages
    ReactDOM.unmountComponentAtNode(errorContainer)
    errorContainer.innerHTML = ''

    renderReactElement(createElement(App, appProps), appContainer)

    emitterProp.emit('after-reactdom-render', { Component, ErrorComponent, appProps })
  } catch (err) {
    if (err.abort) return
    await renderError(err)
  }
}

// This method handles all runtime and debug errors.
// 404 and 500 errors are special kind of errors
// and they are still handle via the main render method.
export async function renderError (error) {
  // We need to unmount the current app component because it's
  // in the inconsistant state.
  // Otherwise, we need to face issues when the issue is fixed and
  // it's get notified via HMR
  ReactDOM.unmountComponentAtNode(appContainer)

  console.error(error)

  const initProps = { err: error, pathname, query, asPath }
  const props = await loadGetInitialProps(ErrorComponent, initProps)
  renderReactElement(createElement(ErrorComponent, props), errorContainer)

  appContainer.innerHTML = ''
}

let isInitialRender = true
function renderReactElement (reactEl, domEl) {
  if (isInitialRender) {
    ReactDOM.hydrate(reactEl, domEl)
    isInitialRender = false
  } else {
    ReactDOM.render(reactEl, domEl)
  }
}
<|endoftext|>