<commit_before>/*
        Sorts matrix like from something like this:
                [
                        [0, 0, 0],
                        [0, 0, 0],
                        [0, 2, 1],
                        [0, 1, 3],
                        [1, 2, 3],
                        [0, 0, 3]
                ]
        to this:
                [
                        [1, 2, 3],
                        [0, 1, 3],
                        [0, 2, 1],
                        [0, 0, 0],
                        [0, 0, 0],
                        [0, 0, 3]
                ]

        The reason why the [0, 0, 3] is last is because
        the vector has the length of 3, so only
        3 vectors are sorted and the rest (irrelevent vectors) are appended
        later.
 */
function sort_reduced_matrix(matrix) {
        'use strict';
        var i, j, len, has_pivot, irrelevant, positions, new_matrix, count;

        len       = {};
        len.i     = matrix.length;    // matrix length (row)
        len.j     = matrix[0].length; // vector length (column)
        positions = [];

        has_pivot = [];

        // Find pivot positions
        for (j = 0; j < len.j; j += 1) {
                for (i = 0; i < len.i; i += 1) {
                        if (matrix[i][j] === 1 && has_pivot[i] !== i) {
                                has_pivot[i] = i;
                                positions[positions.length] = i;
                                break;
                        }
                }
        }

        irrelevant = [];
        count      = 0;

        // Find irrelevant vectors positions
        for (i = 0; i < len.i; i += 1) {
                if (has_pivot[i] === undefined) {
                        irrelevant[count] = i;
                        count += 1;
                }
        }

        new_matrix = [];
        count      = 0;

        // Sort positions
        for (i = 0; i < len.i; i += 1) {
                if (matrix[positions[i]] !== undefined) {
                        new_matrix[i] = matrix[positions[i]];
                } else {
                        new_matrix[i] = matrix[irrelevant[count]];
                        count += 1;
                }
        }

        return new_matrix;
}

function reduced_row_echolon_form(matrix) {
        'use strict';
        var i, p, tmp, len, mu, mv;

        len = {}; // Length.
        i   = {}; // Increment.
        tmp = {}; // Temporary holder.
        p   = {}; // Position.

        len.r   = matrix.length;    // Row, length.
        len.c   = matrix[0].length; // column, length.

        i.r  = 0; // Row, increment.
        i.r2 = 0; // Row2, increment.
        i.c  = 0; // Column, increment.

        tmp.v = []; // Vector, temporary holder.
        tmp.p = 0;  // pivot value.

        p.lp  = 0;  // Lead pivot, position.
        p.rpd = []; // Reserved positions direct, position.

         // Find lead pivots in matrix.
        for (i.r = 0; i.r < len.r; i.r += 1) {

                p.lp = null;
                // Get lead pivot position.
                for (i.c = 0; i.c < len.c; i.c += 1) {
                        /* If position is not reserved nor is zero, then that is
                         * our leading pivot. */
                        if (matrix[i.r][i.c] !== 0 && p.rpd[i.c] === undefined) {
                                p.lp = i.c;
                                break;
                        }
                }

                if (p.lp !== null) {
                        // Reserve lead pivot position.
                        p.rpd[p.lp] = p.lp;
                        // Reduce row such that the pivot is 1.
                        if (matrix[i.r][p.lp] !== 1) {
                                tmp.p = matrix[i.r][p.lp];
                                for (i.c = 0; i.c < len.c; i.c += 1) {
                                        matrix[i.r][i.c] /= tmp.p;
                                }
                        }
                        /* Reduce other rows (i.r2) from row (i.r). */
                        for (i.r2 = 0; i.r2 < len.r; i.r2 += 1) {
                                /* Skip row (i.r) and don't reduce if desired
                                 * value is already zero. */
                                if (i.r2 !== i.r && matrix[i.r2][p.lp] !== 0) {
                                        /* Scale row (i.r) using pivot position
                                         * from row (i.r2) as the multiplier. */
                                        for (i.c = 0; i.c < len.c; i.c += 1) {
                                                tmp.v[i.c] = matrix[i.r][i.c];
                                                tmp.v[i.c] *= matrix[i.r2][p.lp];
                                        }
                                        // Row reduction.
                                        for (i.c = 0; i.c < len.c; i.c += 1) {
                                                matrix[i.r2][i.c] -= tmp.v[i.c];
                                        }
                                }
                        }
                }
        }
        // Finally, we sort our rows, keeping zeros at the bottom and return.
        return sort_reduced_matrix(matrix);
}

// Compere this to wolframalpha.com answers.

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{5%2C+-7%2C+-8%2C+-4}%2C{2%2C+8%2C+-22%2C+-55}%2C+{-3%2C+0%2C+-36%2C+12}}
var matrix = [
        [5, -7, -8, -4],
        [2, 8, -22, -55],
        [-3, 0, -36, 12]
];

matrix = reduced_row_echolon_form(matrix);

console.log(matrix);

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{5%2C+-23%2C+2%2C+4%2C+5%2C+11}%2C{4%2C+-3%2C+6%2C+4%2C+5%2C+2}%2C{3%2C+7%2C+-18%2C+7%2C+9%2C+-6}%2C{4%2C+87%2C+-12%2C+7%2C+12%2C+6}%2C{5%2C+4%2C+7%2C+11%2C+7%2C+-7}}
matrix = [
        [5, -23, 2, 4, 5, 11],
        [4, -3, 6, 4, 5, 2],
        [3, 7, -18, 7, 9, -6],
        [4, 87, -12, 7, 12, 6],
        [5, 4, 7, 11, 7, -7]
];

matrix = reduced_row_echolon_form(matrix);
console.log(matrix[0]);
console.log(matrix[1]);
console.log(matrix[2]);
console.log(matrix[3]);
console.log(matrix[4]);

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{1%2C+2%2C+2%2C+2}%2C{1%2C+3%2C+3%2C+3}%2C+{1%2C+4%2C+16%2C+5}}
matrix = [
        [1, 2, 2, 2],
        [1, 3, 3, 3],
        [1, 4, 16, 5]
];

matrix = reduced_row_echolon_form(matrix);
console.log(matrix);

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{0%2C+2%2C+-1%2C+-6}%2C{0%2C+3%2C+-2%2C+-16}%2C+{0%2C+0%2C+-3%2C+11}}
matrix = [
        [0, 2, -1, -6],
        [0, 3, -2, -16],
        [0, 0, -3, 11]
];

matrix = reduced_row_echolon_form(matrix);
console.log(matrix);<commit_msg>	modified:   to_echelon_form/source.js<commit_after>/*jslint browser: true, indent: 8 */
/*global console, LA, alert*/

/*
        Sorts matrix like from something like this:
                [
                        [0, 0, 0],
                        [0, 0, 0],
                        [0, 2, 1],
                        [0, 1, 3],
                        [1, 2, 3],
                        [0, 0, 3]
                ]
        to this:
                [
                        [1, 2, 3],
                        [0, 1, 3],
                        [0, 2, 1],
                        [0, 0, 0],
                        [0, 0, 0],
                        [0, 0, 3]
                ]

        The reason why the [0, 0, 3] is last is because
        the vector has the length of 3, so only
        3 vectors are sorted and the rest (irrelevent vectors) are appended
        later.
 */
function sort_reduced_matrix(matrix) {
        'use strict';
        var i, j, len, has_pivot, irrelevant, positions, new_matrix, count;

        len       = {};
        len.i     = matrix.length;    // matrix length (row)
        len.j     = matrix[0].length; // vector length (column)
        positions = [];

        has_pivot = [];

        // Find pivot positions
        for (j = 0; j < len.j; j += 1) {
                for (i = 0; i < len.i; i += 1) {
                        if (matrix[i][j] === 1 && has_pivot[i] !== i) {
                                has_pivot[i] = i;
                                positions[positions.length] = i;
                                break;
                        }
                }
        }

        irrelevant = [];
        count      = 0;

        // Find irrelevant vectors positions
        for (i = 0; i < len.i; i += 1) {
                if (has_pivot[i] === undefined) {
                        irrelevant[count] = i;
                        count += 1;
                }
        }

        new_matrix = [];
        count      = 0;

        // Sort positions
        for (i = 0; i < len.i; i += 1) {
                if (matrix[positions[i]] !== undefined) {
                        new_matrix[i] = matrix[positions[i]];
                } else {
                        new_matrix[i] = matrix[irrelevant[count]];
                        count += 1;
                }
        }

        return new_matrix;
}

function reduced_row_echolon_form(matrix) {
        'use strict';
        var i, p, tmp, len, mu, mv;

        len = {}; // Length.
        i   = {}; // Increment.
        tmp = {}; // Temporary holder.
        p   = {}; // Position.

        len.r   = matrix.length;    // Row, length.
        len.c   = matrix[0].length; // column, length.

        i.r  = 0; // Row, increment.
        i.r2 = 0; // Row2, increment.
        i.c  = 0; // Column, increment.

        tmp.v = []; // Vector, temporary holder.
        tmp.p = 0;  // pivot value.

        p.lp  = 0;  // Lead pivot, position.
        p.rpd = []; // Reserved positions direct, position.

         // Find lead pivots in matrix.
        for (i.r = 0; i.r < len.r; i.r += 1) {

                p.lp = null;
                // Get lead pivot position.
                for (i.c = 0; i.c < len.c; i.c += 1) {
                        /* If position is not reserved nor is zero, then that is
                         * our leading pivot. */
                        if (matrix[i.r][i.c] !== 0 && p.rpd[i.c] === undefined) {
                                p.lp = i.c;
                                break;
                        }
                }

                if (p.lp !== null) {
                        // Reserve lead pivot position.
                        p.rpd[p.lp] = p.lp;
                        // Reduce row such that the pivot is 1.
                        if (matrix[i.r][p.lp] !== 1) {
                                tmp.p = matrix[i.r][p.lp];
                                for (i.c = 0; i.c < len.c; i.c += 1) {
                                        matrix[i.r][i.c] /= tmp.p;
                                }
                        }
                        /* Reduce other rows (i.r2) from row (i.r). */
                        for (i.r2 = 0; i.r2 < len.r; i.r2 += 1) {
                                /* Skip row (i.r) and don't reduce if desired
                                 * value is already zero. */
                                if (i.r2 !== i.r && matrix[i.r2][p.lp] !== 0) {
                                        /* Scale row (i.r) using pivot position
                                         * from row (i.r2) as the multiplier. */
                                        for (i.c = 0; i.c < len.c; i.c += 1) {
                                                tmp.v[i.c] = matrix[i.r][i.c];
                                                tmp.v[i.c] *= matrix[i.r2][p.lp];
                                        }
                                        // Row reduction.
                                        for (i.c = 0; i.c < len.c; i.c += 1) {
                                                matrix[i.r2][i.c] -= tmp.v[i.c];
                                        }
                                }
                        }
                }
        }
        // Finally, we sort our rows, keeping zeros at the bottom and return.
        return sort_reduced_matrix(matrix);
}

// Compere this to wolframalpha.com answers.

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{5%2C+-7%2C+-8%2C+-4}%2C{2%2C+8%2C+-22%2C+-55}%2C+{-3%2C+0%2C+-36%2C+12}}
var matrix = [
        [5, -7, -8, -4],
        [2, 8, -22, -55],
        [-3, 0, -36, 12]
];

matrix = reduced_row_echolon_form(matrix);

console.log(matrix);

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{5%2C+-23%2C+2%2C+4%2C+5%2C+11}%2C{4%2C+-3%2C+6%2C+4%2C+5%2C+2}%2C{3%2C+7%2C+-18%2C+7%2C+9%2C+-6}%2C{4%2C+87%2C+-12%2C+7%2C+12%2C+6}%2C{5%2C+4%2C+7%2C+11%2C+7%2C+-7}}
matrix = [
        [5, -23, 2, 4, 5, 11],
        [4, -3, 6, 4, 5, 2],
        [3, 7, -18, 7, 9, -6],
        [4, 87, -12, 7, 12, 6],
        [5, 4, 7, 11, 7, -7]
];

matrix = reduced_row_echolon_form(matrix);
console.log(matrix[0]);
console.log(matrix[1]);
console.log(matrix[2]);
console.log(matrix[3]);
console.log(matrix[4]);

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{1%2C+2%2C+2%2C+2}%2C{1%2C+3%2C+3%2C+3}%2C+{1%2C+4%2C+16%2C+5}}
matrix = [
        [1, 2, 2, 2],
        [1, 3, 3, 3],
        [1, 4, 16, 5]
];

matrix = reduced_row_echolon_form(matrix);
console.log(matrix);

// answer: http://www.wolframalpha.com/input/?i=solve+row+echelon+form+{{0%2C+2%2C+-1%2C+-6}%2C{0%2C+3%2C+-2%2C+-16}%2C+{0%2C+0%2C+-3%2C+11}}
matrix = [
        [0, 2, -1, -6],
        [0, 3, -2, -16],
        [0, 0, -3, 11]
];

matrix = reduced_row_echolon_form(matrix);
console.log(matrix);<|endoftext|>


<commit_before>/ Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Licensed under the Apache-2.0 License on an "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.

// ABOUT THIS NODE.JS SAMPLE: This sample is part of the SDK for JavaScript Developer Guide topic at
// https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/getting-started-nodejs.html

// Load the SDK and UUID
var AWS = require('aws-sdk');
var uuid = require('uuid');

// Create unique bucket name
var bucketName = 'node-sdk-sample-' + uuid.v4();
// Create name for uploaded object key
var keyName = 'hello_world.txt';

// Create a promise on S3 service object
var bucketPromise = new AWS.S3({apiVersion: '2006-03-01'}).createBucket({Bucket: bucketName}).promise();

// Handle promise fulfilled/rejected states
bucketPromise.then(
  function(data) {
    // Create params for putObject call
    var objectParams = {Bucket: bucketName, Key: keyName, Body: 'Hello World!'};
    // Create object upload promise
    var uploadPromise = new AWS.S3({apiVersion: '2006-03-01'}).putObject(objectParams).promise();
    uploadPromise.then(
      function(data) {
        console.log("Successfully uploaded data to " + bucketName + "/" + keyName);
      });
}).catch(
  function(err) {
    console.error(err, err.stack);
});
<commit_msg>Add a backslash for a comment<commit_after>// Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Licensed under the Apache-2.0 License on an "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.

// ABOUT THIS NODE.JS SAMPLE: This sample is part of the SDK for JavaScript Developer Guide topic at
// https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/getting-started-nodejs.html

// Load the SDK and UUID
var AWS = require('aws-sdk');
var uuid = require('uuid');

// Create unique bucket name
var bucketName = 'node-sdk-sample-' + uuid.v4();
// Create name for uploaded object key
var keyName = 'hello_world.txt';

// Create a promise on S3 service object
var bucketPromise = new AWS.S3({apiVersion: '2006-03-01'}).createBucket({Bucket: bucketName}).promise();

// Handle promise fulfilled/rejected states
bucketPromise.then(
  function(data) {
    // Create params for putObject call
    var objectParams = {Bucket: bucketName, Key: keyName, Body: 'Hello World!'};
    // Create object upload promise
    var uploadPromise = new AWS.S3({apiVersion: '2006-03-01'}).putObject(objectParams).promise();
    uploadPromise.then(
      function(data) {
        console.log("Successfully uploaded data to " + bucketName + "/" + keyName);
      });
}).catch(
  function(err) {
    console.error(err, err.stack);
});
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Component.extend({

  actions: {
    createPhoto: function (data) {
      this.sendAction('createPhoto', data);
    },

    removePhoto: function (photo, destroy) {
      this.sendAction('removePhoto', photo, destroy);
    },

    categorize: function (photo, category) {
      const bilde = this.get('bilder').findBy('id', photo.get('id'));
      bilde.set('kategori', category);
      this.reorderPhotos();
    }
  },

  SESONG_CHOICES: [
    Ember.Object.create({value: 'sommer', label: 'Sommer'}),
    Ember.Object.create({value: 'vinter', label: 'Vinter'})
  ],

  KATEGORI_CHOICES: [
    Ember.Object.create({value: 'sommer', label: 'Sommer'}),
    Ember.Object.create({value: 'vinter', label: 'Vinter'}),
    Ember.Object.create({value: 'interiør', label: 'Innendørs'})
  ],

  reorderPhotos: Ember.observer('sesong', function () {
    const bilder = Ember.copy(this.get('bilder').toArray());
    const kategorier = this.get('KATEGORI_CHOICES').getEach('value');

    const reordered = bilder.sort(function (a, b) {
      let kategoriA = a.get('kategori');
      let kategoriB = b.get('kategori');

      if (!kategoriA && !kategoriB) {
        return 0; // Was 1, not sure if this is correct, but fixed uncategorized from being reversed when switching season

      } else if (!kategoriA) {
        return 1;

      } else if (!kategoriB) {
        return -1;

      } else {
        return kategorier.indexOf(kategoriA) - kategorier.indexOf(kategoriB);
      }

    });

    this.set('bilder', bilder);
  }),

  sesong: undefined,

  onBilderLoaded: Ember.observer('bilder.isFulfilled', function () {
    if (this.get('bilder.isFulfilled')) {
      if (!this.get('sesong')) {
        this.set('sesong', this.get('bilder.firstObject.kategori'));
      }
      // this.reorderPhotos();
    }
  }),

  onSesongChange: Ember.observer('sesong', function () {
    const kategorier = this.get('KATEGORI_CHOICES').toArray();
    const sesong = this.get('sesong');
    const sesongObject = kategorier.findBy('value', sesong);

    kategorier.removeObject(sesongObject);
    kategorier.unshiftObject(sesongObject);

    this.set('KATEGORI_CHOICES', kategorier);
  }),

  _sesong: Ember.computed('bilder.firstObject.kategori', {
    get: function () {
      let kategorier = this.get('KATEGORI_CHOICES').getEach('value');
      const sesonger = this.get('SESONG_CHOICES').getEach('value');
      let firstObjectKategori = this.get('bilder.firstObject.kategori');
      let sesong;

      if (sesonger.indexOf(firstObjectKategori) === -1) {
        sesong = sesonger.get('firstObject');

      } else {
        sesong = firstObjectKategori;
      }

      return sesong;
    },
    set: function (key, value) {
      const kategorier = this.get('KATEGORI_CHOICES').toArray();
      const sesong = kategorier.findBy('value', value);

      kategorier.removeObject(sesong);
      kategorier.unshiftObject(sesong);

      this.set('KATEGORI_CHOICES', kategorier);

      return value;
    }
  })

});
<commit_msg>Renamed method more descriptive<commit_after>import Ember from 'ember';

export default Ember.Component.extend({

  actions: {
    createPhoto: function (data) {
      this.sendAction('createPhoto', data);
    },

    removePhoto: function (photo, destroy) {
      this.sendAction('removePhoto', photo, destroy);
    },

    categorize: function (photo, category) {
      const bilde = this.get('bilder').findBy('id', photo.get('id'));
      bilde.set('kategori', category);
      this.reorderPhotosByCategory();
    }
  },

  SESONG_CHOICES: [
    Ember.Object.create({value: 'sommer', label: 'Sommer'}),
    Ember.Object.create({value: 'vinter', label: 'Vinter'})
  ],

  KATEGORI_CHOICES: [
    Ember.Object.create({value: 'sommer', label: 'Sommer'}),
    Ember.Object.create({value: 'vinter', label: 'Vinter'}),
    Ember.Object.create({value: 'interiør', label: 'Innendørs'})
  ],

  reorderPhotosByCategory: Ember.observer('sesong', function () {
    const bilder = Ember.copy(this.get('bilder').toArray());
    const kategorier = this.get('KATEGORI_CHOICES').getEach('value');

    const reordered = bilder.sort(function (a, b) {
      let kategoriA = a.get('kategori');
      let kategoriB = b.get('kategori');

      if (!kategoriA && !kategoriB) {
        return 0; // Was 1, not sure if this is correct, but fixed uncategorized from being reversed when switching season

      } else if (!kategoriA) {
        return 1;

      } else if (!kategoriB) {
        return -1;

      } else {
        return kategorier.indexOf(kategoriA) - kategorier.indexOf(kategoriB);
      }

    });

    this.set('bilder', bilder);
  }),

  sesong: undefined,

  onBilderLoaded: Ember.observer('bilder.isFulfilled', function () {
    if (this.get('bilder.isFulfilled')) {
      if (!this.get('sesong')) {
        this.set('sesong', this.get('bilder.firstObject.kategori'));
      }
      // this.reorderPhotos();
    }
  }),

  onSesongChange: Ember.observer('sesong', function () {
    const kategorier = this.get('KATEGORI_CHOICES').toArray();
    const sesong = this.get('sesong');
    const sesongObject = kategorier.findBy('value', sesong);

    kategorier.removeObject(sesongObject);
    kategorier.unshiftObject(sesongObject);

    this.set('KATEGORI_CHOICES', kategorier);
  }),

  _sesong: Ember.computed('bilder.firstObject.kategori', {
    get: function () {
      let kategorier = this.get('KATEGORI_CHOICES').getEach('value');
      const sesonger = this.get('SESONG_CHOICES').getEach('value');
      let firstObjectKategori = this.get('bilder.firstObject.kategori');
      let sesong;

      if (sesonger.indexOf(firstObjectKategori) === -1) {
        sesong = sesonger.get('firstObject');

      } else {
        sesong = firstObjectKategori;
      }

      return sesong;
    },
    set: function (key, value) {
      const kategorier = this.get('KATEGORI_CHOICES').toArray();
      const sesong = kategorier.findBy('value', value);

      kategorier.removeObject(sesong);
      kategorier.unshiftObject(sesong);

      this.set('KATEGORI_CHOICES', kategorier);

      return value;
    }
  })

});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import {NavLink} from 'fluxible-router';
import {connectToStores} from 'fluxible-addons-react';
import ActivityFeedStore from '../../../stores/ActivityFeedStore';
import ContentStore from '../../../stores/ContentStore';
import {isLocalStorageOn} from '../../../common.js';
import ReactList from 'react-list';
import { Icon, Button, Popup, Grid, Divider, Input} from 'semantic-ui-react';
import { Microservices } from '../../../configs/microservices';
import {isEmpty} from '../../../common';

class PresentationsPanel extends React.Component {

    getPresentationHref(){
        let presLocation = '/presentation/' + this.props.ContentStore.selector.id + '/';
        if(!this.props.ContentStore.selector.subdeck){

            presLocation += this.props.ContentStore.selector.id + '/';
        }
        else{
            presLocation += this.props.ContentStore.selector.subdeck + '/';
        }
        if(this.props.ContentStore.selector.stype === 'slide'){
            // presLocation += this.props.ContentStore.selector.sid + '/';
            presLocation += this.props.ContentStore.selector.sid;// + '/';
        }
        return presLocation;
    }
    openChooseASessionModal(e){
        let modalContent = <Grid columns={2} divided padded>
          <Grid.Column width={8}>
            <Button icon labelPosition="left" onClick={this.openPRModal.bind(this)} aria-label="Create a presentation room, beta"><Icon name="record" size="large"/> Presentation Room</Button>

This will immediately create a Presentation Room, open the room as a new tab and hand you a link to invite attendees. Partcipants will hear your voice, have a one way chat as well as a live subtitle. Presentation Rooms are always public and listed at the deck.

Please test that a Presentation Room is working for you, as we encountered that Presentation Rooms are currently not working on strange network setups.

          </Grid.Column>
          <Grid.Column width={8}>
            <Button icon labelPosition="left" onClick={this.openFAModal.bind(this)} aria-label="Create a follow along, beta"><Icon name="share alternate" size="large"/> Follow Along</Button>

This will create two links with wich you can schedule a Follow Along and invite attendees before hand. Follow Alongs only support to share the presentation progress, but not your voice. A follow along may only be used if everyone is in the same room.

          </Grid.Column>
        </Grid>;
        if(process.env.BROWSER){
            e.preventDefault();
            swal({
                titleText: 'Choose a session type',
                html: '

',
                showCancelButton: true,
                cancelButtonText: 'Close',
                showConfirmButton: false,
                allowOutsideClick: false,
                width: '40rem',
                onOpen: () => {
                    ReactDOM.render(modalContent, document.getElementById('session-modal'));
                }
            }).catch((e) => {return true;});
        }
    }

    openPRModal(){
        swal({
            titleText: 'Please enter a room name',
            input: 'text',
            showCancelButton: true,
            confirmButtonText: 'Next',
            allowOutsideClick: false
        }).then((roomName) => {
            if (!isEmpty(roomName))
                window.open('/presentationbroadcast?room=' + roomName + '&presentation=' + this.getPresentationHref().replace('#', '%23'));
            else
              swal({title: 'Please enter a valid room name', showConfirmButton: false, timer: 2000}).catch(() => {return true;});
        }).catch((e) => {return true;});
    }

    openFAModal(){
        swal({
            titleText: 'Follow Along links & instructions',
            html: '

',
            confirmButtonText: 'Okay',
            allowOutsideClick: false,
            width: '50rem',
            onOpen: () => {
                swal.showLoading();
                $.get(Microservices.webrtc.uri + '/token').done((data) => {
                    console.log(data);
                    let masterLink = window.location.origin + this.getPresentationHref() + '?id=' + data.socketId + '&secret=' + data.secret;
                    let slaveLink = window.location.origin + this.getPresentationHref() + '?id=' + data.socketId;
                    let modalContent =

This is the URL for the presenter of the Follow Along. Only the presenter should use this URL and keep it secret.

                      <Input id='masterLinkInput' fluid defaultValue={masterLink} labelPosition='right' label={<Button id='masterLink' icon onClick={copyURLToClipboardAndIndicate.bind(this, masterLink, '#masterLink')}> <Icon.Group> <Icon name='clipboard outline' /><Icon corner name='arrow left' /></Icon.Group></Button>} onClick={() => $('#masterLinkInput').select()}/>

This is the URL intended for sharing the Follow Along with others. This URL may be published.

                      <Input id='slaveLinkInput' fluid defaultValue={slaveLink} labelPosition='right' label={<Button id='slaveLink' icon onClick={copyURLToClipboardAndIndicate.bind(this, slaveLink, '#slaveLink')}> <Icon.Group> <Icon name='clipboard outline' /><Icon corner name='arrow left' /></Icon.Group></Button>} onClick={() => $('#slaveLinkInput').select()}/>

Please keep both URLs safe until you finished your session. If a URL is lost you must create a new URL pair, as these URLs must match each other.

;
                    ReactDOM.render(modalContent, document.getElementById('session-modal'));
                    swal.hideLoading();
                }).fail((jqXHR, textStatus, error) => {
                    console.log(textStatus);
                });
            }
        }).catch((e) => {return true;});

        function copyURLToClipboardAndIndicate(link, selector) {
            let text = 'Copied!';
            if(!copyURLToClipboard(link))
                text = 'Please copy manually!';
            $(selector).popup({content: text, movePopup: false, on: 'manual', hoverable: true, closable: false, hideOnScroll: false, boundary: '#masterSlaveText', position: 'left center'});
            $(selector).popup('show');
            this.timeout = setTimeout(() => {
                $(selector).popup('hide');
            }, 2000);
        }

        function copyURLToClipboard(href) {
            let toCopy = document.createElement('input');
            toCopy.style.position = 'fixed';
            toCopy.style.top = 0;
            toCopy.style.left = 0;
            toCopy.style.width = '2em';
            toCopy.style.height = '2em';
            toCopy.style.padding = 0;
            toCopy.style.border = 'none';
            toCopy.style.outline = 'none';
            toCopy.style.boxShadow = 'none';
            toCopy.style.background = 'transparent';
            toCopy.value = href;
            document.body.appendChild(toCopy);
            toCopy.value = href;
            toCopy.select();

            try {
                let successful = document.execCommand('copy');
                if(!successful)
                    return false;
                else
                    return true;
            } catch (err) {
                console.log('Oops, unable to copy');
                return false;
            } finally {
                document.body.removeChild(toCopy);
            }
        }
    }

    renderItem(index, key) {
        let url = '/presentationbroadcast?room='+this.props.ActivityFeedStore.presentations[index].roomName+'&presentation=/Presentation/'+this.props.ActivityFeedStore.selector.sid;
        return (

              <Icon name="record" size="large"/>
                {this.props.ActivityFeedStore.presentations[index].roomName} (opened at {new Date(this.props.ActivityFeedStore.presentations[index].openingTime).toLocaleTimeString()})

        );
    }

    render() {
        const panelDIVStyles = {
            maxHeight: 280,
            overflowY: 'auto'
        };

        return (

                <Icon name="warning sign" color="yellow"/> Beta feature

                    <Grid columns={2}>
                      <Grid.Column width={4}>
                        <Popup trigger={<Button icon onClick={this.openChooseASessionModal.bind(this)} aria-label="Create a live session, Beta"><Icon name="record" size="large"/></Button>} content='Create a live session (beta)' />
                      </Grid.Column>
                      <Grid.Column width={12}>
                        Create a live session for this deck and invite participants
                      </Grid.Column>
                    </Grid>
                    <Divider />

Live Sessions

                        {(this.props.ActivityFeedStore.presentations.length < 1)
                            ?

There are currently no live sessions for this deck

                            :
                            <ReactList ref="infiniteList" className="ui list"
                                itemRenderer={this.renderItem.bind(this)}
                                length={this.props.ActivityFeedStore.presentations.length}
                                type={'simple'}>
                            </ReactList>
                        }

        );
    }
}

PresentationsPanel.contextTypes = {
    executeAction: PropTypes.func.isRequired
};
PresentationsPanel = connectToStores(PresentationsPanel, [ActivityFeedStore, ContentStore], (context, props) => {
    return {
        ActivityFeedStore: context.getStore(ActivityFeedStore).getState(),
        ContentStore: context.getStore(ContentStore).getState()
    };
});
export default PresentationsPanel;
<commit_msg>Implemented ajames suggestions<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import {NavLink} from 'fluxible-router';
import {connectToStores} from 'fluxible-addons-react';
import ActivityFeedStore from '../../../stores/ActivityFeedStore';
import ContentStore from '../../../stores/ContentStore';
import {isLocalStorageOn} from '../../../common.js';
import ReactList from 'react-list';
import { Icon, Button, Popup, Grid, Divider, Input, Segment} from 'semantic-ui-react';
import { Microservices } from '../../../configs/microservices';
import {isEmpty} from '../../../common';

class PresentationsPanel extends React.Component {

    getPresentationHref(){
        let presLocation = '/presentation/' + this.props.ContentStore.selector.id + '/';
        if(!this.props.ContentStore.selector.subdeck){

            presLocation += this.props.ContentStore.selector.id + '/';
        }
        else{
            presLocation += this.props.ContentStore.selector.subdeck + '/';
        }
        if(this.props.ContentStore.selector.stype === 'slide'){
            // presLocation += this.props.ContentStore.selector.sid + '/';
            presLocation += this.props.ContentStore.selector.sid;// + '/';
        }
        return presLocation;
    }
    openChooseASessionModal(e){
        let modalContent = <Grid columns={1} divided="vertically" padded>
          <Grid.Row>

Presentation Room

Broadcast your slides to attendees in other locations. Attendees will hear your voice and be able to send you messages.

The Presentation Room will open a new tab. Presentation Rooms are public and listed on the deck. You will also see an link to invite attendees. You should test this feature before sharing your room as network security may prevent others from accessing it.

<Button icon labelPosition="left" floated='right' size='large' onClick={this.openPRModal.bind(this)} aria-label="Create a presentation room, beta"><Icon name="record" size="large"/> Presentation Room</Button>

          </Grid.Row>
          <Grid.Row>

Follow Along

Attendees can Follow Along with your slides on their own device when on the same network as slides will be synced to the presenter’s display. You will be provided with two links - a Presenters Link and an Attendees link to share beforehand.

<Button icon labelPosition="left" floated='right' size='large' onClick={this.openFAModal.bind(this)} aria-label="Create a follow along, beta"><Icon name="share alternate" size="large"/> Follow Along</Button>

          </Grid.Row>
        </Grid>;
        if(process.env.BROWSER){
            e.preventDefault();
            swal({
                titleText: 'Choose a session type',
                html: '

',
                showCancelButton: true,
                cancelButtonText: 'Close',
                showConfirmButton: false,
                allowOutsideClick: false,
                width: '40rem',
                onOpen: () => {
                    ReactDOM.render(modalContent, document.getElementById('session-modal'));
                }
            }).catch((e) => {return true;});
        }
    }

    openPRModal(){
        swal({
            titleText: 'Please enter a room name',
            input: 'text',
            showCancelButton: true,
            confirmButtonText: 'Next',
            allowOutsideClick: false
        }).then((roomName) => {
            if (!isEmpty(roomName))
                window.open('/presentationbroadcast?room=' + roomName + '&presentation=' + this.getPresentationHref().replace('#', '%23'));
            else
              swal({title: 'Please enter a valid room name', showConfirmButton: false, timer: 2000}).catch(() => {return true;});
        }).catch((e) => {return true;});
    }

    openFAModal(){
        swal({
            titleText: 'Follow Along links',
            html: '

',
            confirmButtonText: 'Done',
            allowOutsideClick: false,
            width: '50rem',
            onOpen: () => {
                swal.showLoading();
                $.get(Microservices.webrtc.uri + '/token').done((data) => {
                    let masterLink = window.location.origin + this.getPresentationHref() + '?id=' + data.socketId + '&secret=' + data.secret;
                    let slaveLink = window.location.origin + this.getPresentationHref() + '?id=' + data.socketId;
                    let modalContent =

                      <Segment color='grey' inverted style={{textAlign: 'left'}}>
Presenter Link
</Segment>
                      <Input id='masterLinkInput' fluid defaultValue={masterLink} labelPosition='right' label={<Button id='masterLink' icon onClick={copyURLToClipboardAndIndicate.bind(this, masterLink, '#masterLink')}> <Icon.Group> <Icon name='clipboard outline' /><Icon corner name='arrow left' /></Icon.Group></Button>} onClick={() => $('#masterLinkInput').select()}/>

                      <Segment color='grey' inverted style={{textAlign: 'left'}}>
Attendees Link
</Segment>
                      <Input id='slaveLinkInput' fluid defaultValue={slaveLink} labelPosition='right' label={<Button id='slaveLink' icon onClick={copyURLToClipboardAndIndicate.bind(this, slaveLink, '#slaveLink')}> <Icon.Group> <Icon name='clipboard outline' /><Icon corner name='arrow left' /></Icon.Group></Button>} onClick={() => $('#slaveLinkInput').select()}/>

                      <Segment color='red' inverted tertiary style={{textAlign: 'left', color: 'black !important'}}>Keep a copy of both links until you have finished your session. If you loose either links, you must create a new pair of links.</Segment>

;
                    ReactDOM.render(modalContent, document.getElementById('session-modal'));
                    swal.hideLoading();
                }).fail((jqXHR, textStatus, error) => {
                    console.log(textStatus);
                });
            }
        }).catch((e) => {return true;});

        function copyURLToClipboardAndIndicate(link, selector) {
            let text = 'Copied!';
            if(!copyURLToClipboard(link))
                text = 'Please copy manually!';
            $(selector).popup({content: text, movePopup: false, on: 'manual', hoverable: true, closable: false, hideOnScroll: false, boundary: '#masterSlaveText', position: 'left center'});
            $(selector).popup('show');
            this.timeout = setTimeout(() => {
                $(selector).popup('hide');
            }, 2000);
        }

        function copyURLToClipboard(href) {
            let toCopy = document.createElement('input');
            toCopy.style.position = 'fixed';
            toCopy.style.top = 0;
            toCopy.style.left = 0;
            toCopy.style.width = '2em';
            toCopy.style.height = '2em';
            toCopy.style.padding = 0;
            toCopy.style.border = 'none';
            toCopy.style.outline = 'none';
            toCopy.style.boxShadow = 'none';
            toCopy.style.background = 'transparent';
            toCopy.value = href;
            document.body.appendChild(toCopy);
            toCopy.value = href;
            toCopy.select();

            try {
                let successful = document.execCommand('copy');
                if(!successful)
                    return false;
                else
                    return true;
            } catch (err) {
                console.log('Oops, unable to copy');
                return false;
            } finally {
                document.body.removeChild(toCopy);
            }
        }
    }

    renderItem(index, key) {
        let url = '/presentationbroadcast?room='+this.props.ActivityFeedStore.presentations[index].roomName+'&presentation=/Presentation/'+this.props.ActivityFeedStore.selector.sid;
        return (

              <Icon name="record" size="large"/>
                {this.props.ActivityFeedStore.presentations[index].roomName} (opened at {new Date(this.props.ActivityFeedStore.presentations[index].openingTime).toLocaleTimeString()})

        );
    }

    render() {
        const panelDIVStyles = {
            maxHeight: 280,
            overflowY: 'auto'
        };

        return (

                <Icon name="warning sign" color="yellow"/> Beta feature

                    <Grid columns={2}>
                      <Grid.Column width={4}>
                        <Popup trigger={<Button icon onClick={this.openChooseASessionModal.bind(this)} aria-label="Create a live session, Beta"><Icon name="record" size="large"/></Button>} content='Create a live session (beta)' />
                      </Grid.Column>
                      <Grid.Column width={12}>
                        Create a live session for this deck and invite participants
                      </Grid.Column>
                    </Grid>
                    <Divider />

Live Sessions

                        {(this.props.ActivityFeedStore.presentations.length < 1)
                            ?

There are currently no live sessions for this deck

                            :
                            <ReactList ref="infiniteList" className="ui list"
                                itemRenderer={this.renderItem.bind(this)}
                                length={this.props.ActivityFeedStore.presentations.length}
                                type={'simple'}>
                            </ReactList>
                        }

        );
    }
}

PresentationsPanel.contextTypes = {
    executeAction: PropTypes.func.isRequired
};
PresentationsPanel = connectToStores(PresentationsPanel, [ActivityFeedStore, ContentStore], (context, props) => {
    return {
        ActivityFeedStore: context.getStore(ActivityFeedStore).getState(),
        ContentStore: context.getStore(ContentStore).getState()
    };
});
export default PresentationsPanel;
<|endoftext|>


<commit_before>/* Copyright 2012 Ask Bjørn Hansen, Develooper LLC */
/*jshint jquery:true browser:true */
/*globals d3:true, Modernizr:true */

function server_chart(div, data, options) {
    "use strict";

    if (!options) { options = {}; }
    var legend = options.legend;

    var history = data.history;

    $.each(history, function(i,d) {
        d.date = new Date(d.ts * 1000);
    });

    var y_offset_max = d3.max(history.map(function(e){ return e.offset * 1; })),
        y_offset_min = d3.min(history.map(function(e){ return e.offset * 1; }));

    // more than two seconds off and we'll stop showing just how bad
    if (y_offset_max >  2) { y_offset_max =  2; }
    if (y_offset_min < -2) { y_offset_min = -2; }

    $(".graph_desc").show();

    var w = ($(div).data("width")  || 480),
        h = ($(div).data("height") || 246),
        pad_w = 35,
        pad_h = 19,

        y_offset = d3.scale.pow().exponent(0.5).domain([y_offset_max, y_offset_min]).range([0, h]).clamp(true),

        y_score = d3.scale.sqrt().domain([25,-105]).range([0,h]),

        x = d3.time.scale.utc().domain([d3.min(history.map(function(e){ return e.date; })),
                                        d3.max(history.map(function(e){ return e.date; }))
            ])
            .range([0, w]);

    var svg = d3.select(div.get(0))
        .append("svg")
        .attr("width", w + pad_w * 2)
        .attr("height", h + pad_h * 2)
        .append("g")
        .attr("transform", "translate(" + pad_w + "," + pad_h + ")");

    var xrule = svg.selectAll("g.x")
        .data(x.ticks(10))
        .enter().append("g")
        .attr("class", "x");

    xrule.append("line")
        .attr("x1", x)
        .attr("x2", x)
        .attr("y1", 0)
        .attr("y2", h);

    xrule.append("text")
        .attr("x", x)
        .attr("y", h + 3)
        .attr("dy", ".71em")
        .attr("text-anchor", "middle")
        .text(x.tickFormat(10));

    /* offset y lines */
    var yrule = svg.selectAll("g.y")
        .data(y_offset.ticks(7))
        .enter().append("g")
        .attr("class", "y");

    yrule.append("line")
        .attr("x1", 0)
        .attr("x2", w)
        .attr("y1", y_offset)
        .attr("y2", y_offset);

    yrule.append("text")
        .attr("x", -3)
        .attr("y", y_offset)
        .attr("dy", ".35em")
        .attr("text-anchor", "end")
        .text(function(ms) {ms = ms * 1000;
                            return y_offset.tickFormat(0)(ms);
        });

    /* score y lines */
    var yrule_score = svg.selectAll("g.y_scores")
        .data([20,0,-20,-50,-100])
        .enter().append("g")
        .attr("class", "y_score");

    yrule_score.append("line")
        .attr("x1", 0)
        .attr("x2", w)
        .attr("y1", y_score)
        .attr("y2", y_score);

    yrule_score.append("text")
        .attr("x", w + 23)
        .attr("y", y_score)
        .attr("dy", ".35em")
        .attr("text-anchor", "end")
        .text(function(text) { return text; });

    var zero_offset = svg.selectAll("g.zero_offset")
        .data([0])
        .enter().append("g");

    zero_offset.append("line")
         .attr("x1", 0)
         .attr("x2", w)
         .attr("y1", y_offset(0))
         .attr("y2", y_offset(0))
         .attr("class", "x")
         .attr("stroke-width", 2)
         .attr("stroke", "black");

    svg.append("rect")
        .attr("width", w)
        .attr("height", h);

    svg.selectAll("g.scores")
        .data(data.history)
        .enter().append("circle")
        .filter(function(d) { return d.monitor_id ? true : false; })
        .attr("class", "scores monitor_data")
        .attr("r", 2)
        .attr("transform", function(d,i) {
            var tr = "translate(" + x(d.date) + "," + y_score(d.score) + ")";
            return tr;
        })
        .style("fill", function(d) {
            if (!d.monitor_id) {
                return "black";
            }
            if (d.step < -1) {
               return "red";
            }
            else if (d.step < 0) {
               return "orange";
            }
            else {
               return "steelblue";
            }
        })
        .on('mouseover',  fade(0.2))
        .on('mouseout',   fade(1));

    svg.selectAll("g.offsets")
        .data(data.history)
        .enter()
        .append("circle")
        .filter(function(d) { return d.monitor_id ? true : false; })
        .attr("class", "offsets monitor_data")
        .attr("r", 1.5) // todo: make this 2 if number of data points are < 250 or some such
        .attr("transform", function(d,i) { return "translate(" + x(d.date) + "," + y_offset(d.offset) + ")"; })
        .style("fill", function(d) {
            var offset = d.offset;

            if (offset < 0) { offset = offset * -1; }
            if ( offset < 0.050 ) {
                return "green";
            }
            else if ( offset < 0.100 ) {
                return "orange";
            }
            else {
                return "red";
           }
        })
        .on('mouseover',  fade(0.25))
        .on('mouseout',   fade(1));

    var dh = data.history.filter(function(d) { return d.monitor_id === null ? true : false; });
    //console.log("d.history", dh);

    svg.selectAll(".total_score")
        .data([dh])
        .enter().append("path")
        .attr("class", "line total_score")
        .style("fill", "none")
        .style("stoke", "red")
        .style("stroke-width", 2)
        .attr("d", d3.svg.line()
              .x(function(d) { return x(d.date); })
              .y(function(d) { return y_score(d.score); })
             );

   // -----------------------------
   // Add Title then Legend
   // -----------------------------
   svg.append("svg:text")
       .attr("x", 0)
       .attr("y", -5)
       .style("font-weight", "bold")
       .text("Offset monitoring and scores for " + data.server.ip);

    if (legend) {
        legend.css("margin-left", pad_w);
        legend.append('Monitoring Station:');
        for (var i = 0; i < data.monitors.length; i++) {
            var mon = data.monitors[i];
            var text = mon.name + " (" + mon.score + ")";
            legend.append($('').text(text).addClass('legend').data('monitor_id', mon.id));
        }
        var fadeout = fade(0.25);
        var fadein  = fade(1);
        legend.find('span').mouseenter(function(e) {
                                        var mon = $(this).data('monitor_id');
                                        if (!mon) { return; }
                                        fadeout( { monitor_id: mon } );
                                    });
        legend.find('span').mouseleave(function(e) {
                                        var mon = $(this).data('monitor_id');
                                        if (!mon) { return; }
                                        fadein( { monitor_id: mon } );
                                    });
    }

    function fade (opacity) {
        return function(g, i) {
            svg.selectAll(".monitor_data")
               .filter(function(d) {
                   return d.monitor_id !== g.monitor_id;
               })
            .transition()
            .style("opacity", opacity);
        };
    };

}

$(document).ready(function(){
   "use strict";

   var data = {};
   var graph_div = $('div.graph');

   if (!NP.svg_graphs && !Modernizr.svg) { // no svg support, show the noscript section
       var $legacy = $('#legacy-graphs');

       $legacy.html('Please upgrade to a browser that supports SVG '
                    + 'to see the new graphs. '
                    + '(For example Safari, '
                    + 'Chrome, '
                    + 'Firefox or '
                    + 'IE9+)
'
                   );

       $legacy.append($('
')
           .attr('src', $legacy.data('offset-graph-url')));

       return;
   }

    graph_div.each(function(i) {
        var div = $(this);
        var ip = div.data('server-ip');
        if (ip) {
            var graph_legend = div.next('.graph-legend');

            d3.json("/scores/"+ ip +"/json?monitor=*&limit=400", function(json) {
                if (json) {
                    data[ip] = json;
                    server_chart(div, json, { legend: graph_legend });
                }
                else {
                    div.html('
Error downloading graph data
');
                }
            });
        }
    });

});

<commit_msg>Indentation, work better with pages without legacy graph section<commit_after>/* Copyright 2012 Ask Bjørn Hansen, Develooper LLC */
/*jshint jquery:true browser:true */
/*globals d3:true, Modernizr:true */

function server_chart(div, data, options) {
    "use strict";

    if (!options) { options = {}; }
    var legend = options.legend;

    var history = data.history;

    $.each(history, function(i,d) {
        d.date = new Date(d.ts * 1000);
    });

    var y_offset_max = d3.max(history.map(function(e){ return e.offset * 1; })),
        y_offset_min = d3.min(history.map(function(e){ return e.offset * 1; }));

    // more than two seconds off and we'll stop showing just how bad
    if (y_offset_max >  2) { y_offset_max =  2; }
    if (y_offset_min < -2) { y_offset_min = -2; }

    $(".graph_desc").show();

    var w = ($(div).data("width")  || 480),
        h = ($(div).data("height") || 246),
        pad_w = 35,
        pad_h = 19,

        y_offset = d3.scale.pow().exponent(0.5).domain([y_offset_max, y_offset_min]).range([0, h]).clamp(true),

        y_score = d3.scale.sqrt().domain([25,-105]).range([0,h]),

        x = d3.time.scale.utc().domain([d3.min(history.map(function(e){ return e.date; })),
                                        d3.max(history.map(function(e){ return e.date; }))
            ])
            .range([0, w]);

    var svg = d3.select(div.get(0))
        .append("svg")
        .attr("width", w + pad_w * 2)
        .attr("height", h + pad_h * 2)
        .append("g")
        .attr("transform", "translate(" + pad_w + "," + pad_h + ")");

    var xrule = svg.selectAll("g.x")
        .data(x.ticks(10))
        .enter().append("g")
        .attr("class", "x");

    xrule.append("line")
        .attr("x1", x)
        .attr("x2", x)
        .attr("y1", 0)
        .attr("y2", h);

    xrule.append("text")
        .attr("x", x)
        .attr("y", h + 3)
        .attr("dy", ".71em")
        .attr("text-anchor", "middle")
        .text(x.tickFormat(10));

    /* offset y lines */
    var yrule = svg.selectAll("g.y")
        .data(y_offset.ticks(7))
        .enter().append("g")
        .attr("class", "y");

    yrule.append("line")
        .attr("x1", 0)
        .attr("x2", w)
        .attr("y1", y_offset)
        .attr("y2", y_offset);

    yrule.append("text")
        .attr("x", -3)
        .attr("y", y_offset)
        .attr("dy", ".35em")
        .attr("text-anchor", "end")
        .text(function(ms) {ms = ms * 1000;
                            return y_offset.tickFormat(0)(ms);
        });

    /* score y lines */
    var yrule_score = svg.selectAll("g.y_scores")
        .data([20,0,-20,-50,-100])
        .enter().append("g")
        .attr("class", "y_score");

    yrule_score.append("line")
        .attr("x1", 0)
        .attr("x2", w)
        .attr("y1", y_score)
        .attr("y2", y_score);

    yrule_score.append("text")
        .attr("x", w + 23)
        .attr("y", y_score)
        .attr("dy", ".35em")
        .attr("text-anchor", "end")
        .text(function(text) { return text; });

    var zero_offset = svg.selectAll("g.zero_offset")
        .data([0])
        .enter().append("g");

    zero_offset.append("line")
         .attr("x1", 0)
         .attr("x2", w)
         .attr("y1", y_offset(0))
         .attr("y2", y_offset(0))
         .attr("class", "x")
         .attr("stroke-width", 2)
         .attr("stroke", "black");

    svg.append("rect")
        .attr("width", w)
        .attr("height", h);

    svg.selectAll("g.scores")
        .data(data.history)
        .enter().append("circle")
        .filter(function(d) { return d.monitor_id ? true : false; })
        .attr("class", "scores monitor_data")
        .attr("r", 2)
        .attr("transform", function(d,i) {
            var tr = "translate(" + x(d.date) + "," + y_score(d.score) + ")";
            return tr;
        })
        .style("fill", function(d) {
            if (!d.monitor_id) {
                return "black";
            }
            if (d.step < -1) {
               return "red";
            }
            else if (d.step < 0) {
               return "orange";
            }
            else {
               return "steelblue";
            }
        })
        .on('mouseover',  fade(0.2))
        .on('mouseout',   fade(1));

    svg.selectAll("g.offsets")
        .data(data.history)
        .enter()
        .append("circle")
        .filter(function(d) { return d.monitor_id ? true : false; })
        .attr("class", "offsets monitor_data")
        .attr("r", 1.5) // todo: make this 2 if number of data points are < 250 or some such
        .attr("transform", function(d,i) { return "translate(" + x(d.date) + "," + y_offset(d.offset) + ")"; })
        .style("fill", function(d) {
            var offset = d.offset;

            if (offset < 0) { offset = offset * -1; }
            if ( offset < 0.050 ) {
                return "green";
            }
            else if ( offset < 0.100 ) {
                return "orange";
            }
            else {
                return "red";
           }
        })
        .on('mouseover',  fade(0.25))
        .on('mouseout',   fade(1));

    var dh = data.history.filter(function(d) { return d.monitor_id === null ? true : false; });
    //console.log("d.history", dh);

    svg.selectAll(".total_score")
        .data([dh])
        .enter().append("path")
        .attr("class", "line total_score")
        .style("fill", "none")
        .style("stoke", "red")
        .style("stroke-width", 2)
        .attr("d", d3.svg.line()
              .x(function(d) { return x(d.date); })
              .y(function(d) { return y_score(d.score); })
             );

   // -----------------------------
   // Add Title then Legend
   // -----------------------------
   svg.append("svg:text")
       .attr("x", 0)
       .attr("y", -5)
       .style("font-weight", "bold")
       .text("Offset monitoring and scores for " + data.server.ip);

    if (legend) {
        legend.css("margin-left", pad_w);
        legend.append('Monitoring Station:');
        for (var i = 0; i < data.monitors.length; i++) {
            var mon = data.monitors[i];
            var text = mon.name + " (" + mon.score + ")";
            legend.append($('').text(text).addClass('legend').data('monitor_id', mon.id));
        }
        var fadeout = fade(0.25);
        var fadein  = fade(1);
        legend.find('span').mouseenter(function(e) {
                                        var mon = $(this).data('monitor_id');
                                        if (!mon) { return; }
                                        fadeout( { monitor_id: mon } );
                                    });
        legend.find('span').mouseleave(function(e) {
                                        var mon = $(this).data('monitor_id');
                                        if (!mon) { return; }
                                        fadein( { monitor_id: mon } );
                                    });
    }

    function fade (opacity) {
        return function(g, i) {
            svg.selectAll(".monitor_data")
               .filter(function(d) {
                   return d.monitor_id !== g.monitor_id;
               })
            .transition()
            .style("opacity", opacity);
        };
    };

}

$(document).ready(function(){
    "use strict";

    var data = {};
    var graph_div = $('div.graph');

    if (!NP.svg_graphs && !Modernizr.svg) { // no svg support, show the noscript section
        var $legacy = $('#legacy-graphs');

        if (!legacy) { return; }

        $legacy.html('Please upgrade to a browser that supports SVG '
                     + 'to see the new graphs. '
                     + '(For example Safari, '
                     + 'Chrome, '
                     + 'Firefox or '
                     + 'IE9+)
'
                    );

        $legacy.append($('
')
            .attr('src', $legacy.data('offset-graph-url')));

        return;
    }

    graph_div.each(function(i) {
        var div = $(this);
        var ip = div.data('server-ip');
        if (ip) {
            var graph_legend = div.next('.graph-legend');

            d3.json("/scores/"+ ip +"/json?monitor=*&limit=400", function(json) {
                if (json) {
                    data[ip] = json;
                    server_chart(div, json, { legend: graph_legend });
                }
                else {
                    div.html('
Error downloading graph data
');
                }
            });
        }
    });

});

<|endoftext|>


<commit_before>(function() {
  angular.module('docsApp')
    .factory('codepenDataAdapter', CodepenDataAdapter)
    .factory('codepen', ['$demoAngularScripts', '$document', 'codepenDataAdapter', Codepen]);

  // Provides a service to open a code example in codepen.
  function Codepen($demoAngularScripts, $document, codepenDataAdapter) {

    // The following URL used to be HTTP and not HTTPS to allow us to do localhost testing
    // It's no longer working, for more info:
    // https://blog.codepen.io/2017/03/31/codepen-going-https/
    var CODEPEN_API = 'https://codepen.io/pen/define/';

    return {
      editOnCodepen: editOnCodepen
    };

    // Creates a codepen from the given demo model by posting to Codepen's API
    // using a hidden form.  The hidden form is necessary to avoid a CORS issue.
    // See http://blog.codepen.io/documentation/api/prefill
    function editOnCodepen(demo) {
      var externalScripts = $demoAngularScripts.all();
      externalScripts.push('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.js');
      var data = codepenDataAdapter.translate(demo, externalScripts);
      var form = buildForm(data);
      $document.find('body').append(form);
      form[0].submit();
      form.remove();
    }

    // Builds a hidden form with data necessary to create a codepen.
    function buildForm(data) {
      var form = angular.element(
        '<form style="display: none;" method="post" target="_blank" action="' +
          CODEPEN_API +
          '"></form>'
      );
      var input = '<input type="hidden" name="data" value="' + escapeJsonQuotes(data) + '" />';
      form.append(input);
      return form;
    }

    // Recommended by Codepen to escape quotes.
    // See http://blog.codepen.io/documentation/api/prefill
    function escapeJsonQuotes(json) {
      return JSON.stringify(json)
        .replace(/'/g, "&apos;")
        .replace(/"/g, "&quot;")
        /**
         * Codepen was unescaping < (<) and > (>) which caused, on some demos,
         * an unclosed elements (like <md-select>).
         * Used different unicode lookalike characters so it won't be considered as an element
         */
        .replace(/&lt;/g, "˂") // http://graphemica.com/%CB%82
        .replace(/&gt;/g, "˃"); // http://graphemica.com/%CB%83
    }
  }

  // Translates demo metadata and files into Codepen's post form data.  See api documentation for
  // additional fields not used by this service. http://blog.codepen.io/documentation/api/prefill
  function CodepenDataAdapter() {

    // The following URL's need to use `localhost` as these values get replaced during release
    var CORE_JS  = 'http://localhost:8080/angular-material.js';
    var CORE_CSS = 'http://localhost:8080/angular-material.css';
    var DOC_CSS  = 'http://localhost:8080/docs.css';              // CSS overrides for custom docs

    var LINK_FONTS_ROBOTO = '<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">';

    var UNSECURE_CACHE_JS = 'http://ngmaterial.assets.s3.amazonaws.com/svg-assets-cache.js';
    var ASSET_CACHE_JS = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/svg-assets-cache.js';

    return {
      translate: translate
    };

    // Translates a demo model to match Codepen's post data
    // See http://blog.codepen.io/documentation/api/prefill
    function translate(demo, externalScripts) {
      var files = demo.files;

      return appendLicenses({
        title: demo.title,
        html: processHtml(demo),
        head: LINK_FONTS_ROBOTO,

        js: processJs(files.js),
        css: mergeFiles( files.css ).join(' '),

        js_external: externalScripts.concat([ASSET_CACHE_JS, CORE_JS]).join(';'),
        css_external: [CORE_CSS, DOC_CSS].join(';')
      });
    }

    // Modifies index.html with necessary changes in order to display correctly in codepen
    // See each processor to determine how each modifies the html
    function processHtml(demo) {

      var allContent = demo.files.index.contents;

      var processors = [
        applyAngularAttributesToParentElement,
        insertTemplatesAsScriptTags,
        htmlEscapeAmpersand
      ];

      processors.forEach(function(processor) {
        allContent = processor(allContent, demo);
      });

      return allContent;
    }

    /**
     * Append MIT License information to all CodePen source samples(HTML, JS, CSS)
     */
    function appendLicenses(data) {

      data.html = appendLicenseFor(data.html, 'html');
      data.js   = appendLicenseFor(data.js, 'js');
      data.css  = appendLicenseFor(data.css, 'css');

      function appendLicenseFor(content, lang) {
            var commentStart = '', commentEnd = '';

        switch(lang) {
          case 'html' : commentStart = '<!--'; commentEnd = '-->'; break;
          case 'js'   : commentStart = '/**';  commentEnd = '**/'; break;
          case 'css'  : commentStart = '/*';   commentEnd = '*/';  break;
        }

        return content + '\n\n'+
          commentStart + '\n'+
          'Copyright 2018 Google Inc. All Rights Reserved. \n'+
          'Use of this source code is governed by an MIT-style license that can be found'+
          'in the LICENSE file at http://material.angularjs.org/HEAD/license.\n'+
          commentEnd;
      }

      return data;
    }

    // Applies modifications the javascript prior to sending to codepen.
    // Currently merges js files and replaces the module with the Codepen
    // module.  See documentation for replaceDemoModuleWithCodepenModule.
    function processJs(jsFiles) {
      var mergedJs = mergeFiles(jsFiles).join(' ');
      var script = replaceDemoModuleWithCodepenModule(mergedJs);
      return script;
    }

    // Maps file contents to an array
    function mergeFiles(files) {
      return files.map(function(file) {
        return file.contents;
      });
    }

    // Adds class to parent element so that styles are applied correctly
    // Adds ng-app attribute.  This is the same module name provided in the asset-cache.js
    function applyAngularAttributesToParentElement(html, demo) {
      var tmp;

      // Grab only the DIV for the demo...
      angular.forEach(angular.element(html), function(it,key){
        if ((it.nodeName != "SCRIPT") && (it.nodeName != "#text")) {
          tmp = angular.element(it);
        }
      });

      tmp.addClass(demo.id);
      tmp.attr('ng-app', 'MyApp');
      return tmp[0].outerHTML;
    }

    // Adds templates inline in the html, so that templates are cached in the example
    function insertTemplatesAsScriptTags(indexHtml, demo) {
      if (demo.files.html.length) {
        var tmp = angular.element(indexHtml);
        angular.forEach(demo.files.html, function(template) {
          tmp.append("<script type='text/ng-template' id='" +
                     template.name + "'>" +
                     template.contents +
                     "</script>");
        });
        return tmp[0].outerHTML;
      }
      return indexHtml;
    }

    // Escapes ampersands so that after codepen unescapes the html the escaped code block
    // uses the correct escaped characters
    function htmlEscapeAmpersand(html) {
      return html
        .replace(/&/g, "&");
    }

    // Required to make codePen work. Demos define their own module when running on the
    // docs site.  In order to ensure the codepen example can use the svg-asset-cache.js, the
    // module needs to match so that the $templateCache is populated with the necessary
    // assets.

    function replaceDemoModuleWithCodepenModule(file) {
      var matchAngularModule =  /\.module\(('[^']*'|"[^"]*")\s*,(\s*\[([^\]]*)\]\s*\))/ig;
      var modules = "['ngMaterial', 'ngMessages', 'material.svgAssetsCache']";

      // See scripts.js for list of external AngularJS libraries used for the demos

      return file.replace(matchAngularModule, ".module('MyApp', "+ modules + ")");
    }
  }
})();
<commit_msg>docs(codepen): fix missing line break in Copyright (#11367)<commit_after>(function() {
  angular.module('docsApp')
    .factory('codepenDataAdapter', CodepenDataAdapter)
    .factory('codepen', ['$demoAngularScripts', '$document', 'codepenDataAdapter', Codepen]);

  // Provides a service to open a code example in codepen.
  function Codepen($demoAngularScripts, $document, codepenDataAdapter) {

    // The following URL used to be HTTP and not HTTPS to allow us to do localhost testing
    // It's no longer working, for more info:
    // https://blog.codepen.io/2017/03/31/codepen-going-https/
    var CODEPEN_API = 'https://codepen.io/pen/define/';

    return {
      editOnCodepen: editOnCodepen
    };

    // Creates a codepen from the given demo model by posting to Codepen's API
    // using a hidden form.  The hidden form is necessary to avoid a CORS issue.
    // See http://blog.codepen.io/documentation/api/prefill
    function editOnCodepen(demo) {
      var externalScripts = $demoAngularScripts.all();
      externalScripts.push('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.js');
      var data = codepenDataAdapter.translate(demo, externalScripts);
      var form = buildForm(data);
      $document.find('body').append(form);
      form[0].submit();
      form.remove();
    }

    // Builds a hidden form with data necessary to create a codepen.
    function buildForm(data) {
      var form = angular.element(
        '<form style="display: none;" method="post" target="_blank" action="' +
          CODEPEN_API +
          '"></form>'
      );
      var input = '<input type="hidden" name="data" value="' + escapeJsonQuotes(data) + '" />';
      form.append(input);
      return form;
    }

    // Recommended by Codepen to escape quotes.
    // See http://blog.codepen.io/documentation/api/prefill
    function escapeJsonQuotes(json) {
      return JSON.stringify(json)
        .replace(/'/g, "&apos;")
        .replace(/"/g, "&quot;")
        /**
         * Codepen was unescaping < (<) and > (>) which caused, on some demos,
         * an unclosed elements (like <md-select>).
         * Used different unicode lookalike characters so it won't be considered as an element
         */
        .replace(/&lt;/g, "˂") // http://graphemica.com/%CB%82
        .replace(/&gt;/g, "˃"); // http://graphemica.com/%CB%83
    }
  }

  // Translates demo metadata and files into Codepen's post form data.  See api documentation for
  // additional fields not used by this service. http://blog.codepen.io/documentation/api/prefill
  function CodepenDataAdapter() {

    // The following URL's need to use `localhost` as these values get replaced during release
    var CORE_JS  = 'http://localhost:8080/angular-material.js';
    var CORE_CSS = 'http://localhost:8080/angular-material.css';
    var DOC_CSS  = 'http://localhost:8080/docs.css';              // CSS overrides for custom docs

    var LINK_FONTS_ROBOTO = '<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">';

    var UNSECURE_CACHE_JS = 'http://ngmaterial.assets.s3.amazonaws.com/svg-assets-cache.js';
    var ASSET_CACHE_JS = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/svg-assets-cache.js';

    return {
      translate: translate
    };

    // Translates a demo model to match Codepen's post data
    // See http://blog.codepen.io/documentation/api/prefill
    function translate(demo, externalScripts) {
      var files = demo.files;

      return appendLicenses({
        title: demo.title,
        html: processHtml(demo),
        head: LINK_FONTS_ROBOTO,

        js: processJs(files.js),
        css: mergeFiles( files.css ).join(' '),

        js_external: externalScripts.concat([ASSET_CACHE_JS, CORE_JS]).join(';'),
        css_external: [CORE_CSS, DOC_CSS].join(';')
      });
    }

    // Modifies index.html with necessary changes in order to display correctly in codepen
    // See each processor to determine how each modifies the html
    function processHtml(demo) {

      var allContent = demo.files.index.contents;

      var processors = [
        applyAngularAttributesToParentElement,
        insertTemplatesAsScriptTags,
        htmlEscapeAmpersand
      ];

      processors.forEach(function(processor) {
        allContent = processor(allContent, demo);
      });

      return allContent;
    }

    /**
     * Append MIT License information to all CodePen source samples(HTML, JS, CSS)
     */
    function appendLicenses(data) {

      data.html = appendLicenseFor(data.html, 'html');
      data.js   = appendLicenseFor(data.js, 'js');
      data.css  = appendLicenseFor(data.css, 'css');

      function appendLicenseFor(content, lang) {
            var commentStart = '', commentEnd = '';

        switch(lang) {
          case 'html' : commentStart = '<!--'; commentEnd = '-->'; break;
          case 'js'   : commentStart = '/**';  commentEnd = '**/'; break;
          case 'css'  : commentStart = '/*';   commentEnd = '*/';  break;
        }

        return content + '\n\n'+
          commentStart + '\n'+
          'Copyright 2018 Google Inc. All Rights Reserved. \n'+
          'Use of this source code is governed by an MIT-style license that can be found\n'+
          'in the LICENSE file at http://material.angularjs.org/HEAD/license.\n'+
          commentEnd;
      }

      return data;
    }

    // Applies modifications the javascript prior to sending to codepen.
    // Currently merges js files and replaces the module with the Codepen
    // module.  See documentation for replaceDemoModuleWithCodepenModule.
    function processJs(jsFiles) {
      var mergedJs = mergeFiles(jsFiles).join(' ');
      var script = replaceDemoModuleWithCodepenModule(mergedJs);
      return script;
    }

    // Maps file contents to an array
    function mergeFiles(files) {
      return files.map(function(file) {
        return file.contents;
      });
    }

    // Adds class to parent element so that styles are applied correctly
    // Adds ng-app attribute.  This is the same module name provided in the asset-cache.js
    function applyAngularAttributesToParentElement(html, demo) {
      var tmp;

      // Grab only the DIV for the demo...
      angular.forEach(angular.element(html), function(it,key){
        if ((it.nodeName != "SCRIPT") && (it.nodeName != "#text")) {
          tmp = angular.element(it);
        }
      });

      tmp.addClass(demo.id);
      tmp.attr('ng-app', 'MyApp');
      return tmp[0].outerHTML;
    }

    // Adds templates inline in the html, so that templates are cached in the example
    function insertTemplatesAsScriptTags(indexHtml, demo) {
      if (demo.files.html.length) {
        var tmp = angular.element(indexHtml);
        angular.forEach(demo.files.html, function(template) {
          tmp.append("<script type='text/ng-template' id='" +
                     template.name + "'>" +
                     template.contents +
                     "</script>");
        });
        return tmp[0].outerHTML;
      }
      return indexHtml;
    }

    // Escapes ampersands so that after codepen unescapes the html the escaped code block
    // uses the correct escaped characters
    function htmlEscapeAmpersand(html) {
      return html
        .replace(/&/g, "&");
    }

    // Required to make codePen work. Demos define their own module when running on the
    // docs site.  In order to ensure the codepen example can use the svg-asset-cache.js, the
    // module needs to match so that the $templateCache is populated with the necessary
    // assets.

    function replaceDemoModuleWithCodepenModule(file) {
      var matchAngularModule =  /\.module\(('[^']*'|"[^"]*")\s*,(\s*\[([^\]]*)\]\s*\))/ig;
      var modules = "['ngMaterial', 'ngMessages', 'material.svgAssetsCache']";

      // See scripts.js for list of external AngularJS libraries used for the demos

      return file.replace(matchAngularModule, ".module('MyApp', "+ modules + ")");
    }
  }
})();
<|endoftext|>


<commit_before>"use strict";

class gsuiWindow {
	constructor( parent, id ) {
		const root = gsuiWindow.template.cloneNode( true );

		this.id = id;
		this.parent = parent;
		this.rootElement = root;
		this._elWrap = this._getElem( "wrap" );
		this._elHandlers = this._getElem( "handlers" );
		this._show =
		this._minimized =
		this._maximized = false;
		this.zIndex = 0;
		this.onresize =
		this.onfocusin =
		this.onresizing = null;
		this._pos = Object.seal( { x: 0, y: 0 } );
		this._mousedownX =
		this._mousedownY =
		this._mousemoveX =
		this._mousemoveY =
		this._mousedownHeadHeight = 0;
		this._x =
		this._y =
		this._w =
		this._h =
		this.__x =
		this.__y =
		this.__w =
		this.__h = 0;
		Object.seal( this );

		root.dataset.windowId = id;
		root.addEventListener( "focusin", parent._onfocusinWin.bind( parent, this ) );
		this._getElem( "icon" ).ondblclick = this.close.bind( this );
		this._getElem( "headBtns" ).onclick = this._onclickBtns.bind( this );
		this._getElem( "head" ).onmousedown = this._onmousedownHead.bind( this );
		this._getElem( "title" ).ondblclick =
		this._getElem( "headContent" ).ondblclick = this._ondblclickTitle.bind( this );
		this._elHandlers.onmousedown = this._onmousedownHandlers.bind( this );
		this._setZIndex( 0 );
		this.setTitle( id );
		this.setPosition( 0, 0 );
		this.setSize( 300, 150 );
	}

	open() { return this.openToggle( true ); }
	close() { return this.openToggle( false ); }
	openToggle( b ) {
		if ( b !== this._show ) {
			if ( b ) {
				this._show = true;
				this._setClass( "show", true );
				this.parent._open( this );
			} else if ( !this.onclose || this.onclose() !== false ) {
				this._show = false;
				this._setClass( "show", false );
				this.parent._close( this );
			}
		}
	}

	setIdAttr( id ) {
		this.rootElement.id = id;
	}
	setTitleIcon( icon ) {
		this._getElem( "icon" ).dataset.icon = icon;
	}
	empty() {
		const cnt = this._getElem( "content" ),
			headCnt = this._getElem( "headContent" );

		while ( cnt.lastChild ) {
			cnt.lastChild.remove();
		}
		while ( headCnt.lastChild ) {
			headCnt.lastChild.remove();
		}
	}
	append( ...args ) {
		this._getElem( "content" ).append( ...args );
	}
	headAppend( ...args ) {
		this._getElem( "headContent" ).append( ...args );
	}

	focus() {
		const root = this.rootElement;

		if ( !root.contains( document.activeElement ) ) {
			setTimeout( root.focus.bind( root ), 50 );
		}
	}
	maximize() {
		if ( !this._maximized ) {
			const st = this.rootElement.style;

			this.__x = this._x;
			this.__y = this._y;
			this.__w = this._w;
			if ( !this._minimized ) {
				this.__h = this._h;
			}
			st.top = st.left = st.right = st.bottom = st.width = st.height = "";
			this._setClass( "maximized", true );
			this._setClass( "minimized", false );
			this._maximized = true;
			this._minimized = false;
			this._callOnresize();
			this.focus();
			this.parent._winMaximized( this.id );
		}
	}
	minimize() {
		if ( !this._minimized ) {
			if ( !this._maximized ) {
				this.__w = this._w;
				this.__h = this._h;
				this.__x = this._x;
				this.__y = this._y;
			}
			this._setClass( "minimized", true );
			this._setClass( "maximized", false );
			this._minimized = true;
			this._maximized = false;
			this.setSize( this.__w, this._getHeadHeight(), "nocallback" );
			this.setPosition( this.__x, this.__y );
			this.parent._winRestored( this.id );
		}
	}
	restore() {
		if ( this._minimized || this._maximized ) {
			this.focus();
			this._setClass( "minimized", false );
			this._setClass( "maximized", false );
			this._minimized =
			this._maximized = false;
			this.setSize( this.__w, this.__h );
			this.setPosition( this.__x, this.__y );
			this.parent._winRestored( this.id );
		}
	}

	movable( b ) {
		this._setClass( "movable", b );
	}
	setTitle( t ) {
		this._getElem( "title" ).textContent = t;
	}
	setSize( w, h, nocb ) {
		this._w = w;
		this._h = h;
		this.rootElement.style.width = `${ w }px`;
		this.rootElement.style.height = `${ h }px`;
		if ( nocb !== "nocallback" ) {
			this._callOnresize();
		}
	}
	setPosition( x, y ) {
		this._x = x;
		this._y = y;
		this.rootElement.style.left = `${ x }px`;
		this.rootElement.style.top = `${ y }px`;
	}

	// events:
	_onclickBtns( e ) {
		const act = e.target.dataset.icon;

		if ( act ) {
			this[ act ]();
		}
	}
	_ondblclickTitle( e ) {
		if ( e.target === e.currentTarget ) {
			this._maximized
				? this.restore()
				: this.maximize();
		}
	}
	_onmousedownHead( e ) {
		const clTar = e.target.classList,
			clicked =
				clTar.contains( "gsuiWindow-head" ) ||
				clTar.contains( "gsuiWindow-title" ) ||
				clTar.contains( "gsuiWindow-headContent" );

		if ( clicked ) {
			this._mousedownX = e.clientX;
			this._mousedownY = e.clientY;
			this._setClass( "dragging", true );
			this.parent._startMousemoving( "move",
				this._onmousemoveHead.bind( this ),
				this._onmouseupHead.bind( this ) );
		}
	}
	_onmousedownHandlers( e ) {
		const dir = e.target.dataset.dir;

		if ( dir ) {
			this._mousedownX = e.clientX;
			this._mousedownY = e.clientY;
			this._mousedownHeadHeight = this._getHeadHeight();
			this._setClass( "dragging", true );
			this.parent._startMousemoving( `${ dir }-resize`,
				this._onmousemoveHandler.bind( this, dir ),
				this._onmouseupHandler.bind( this, dir ) );
		}
	}
	_onmousemoveHead( e ) {
		const x = e.clientX - this._mousedownX,
			y = e.clientY - this._mousedownY;

		this._mousemoveX = x;
		this._mousemoveY = y;
		this._setCSSrelativeMove( this._elHandlers.style, x, y );
		if ( !this.parent._lowGraphics ) {
			this._setCSSrelativeMove( this._elWrap.style, x, y );
		}
	}
	_onmouseupHead( e ) {
		const x = this._x,
			y = this._y,
			x_ = e.clientX - this._mousedownX,
			y_ = e.clientY - this._mousedownY;

		this._setClass( "dragging", false );
		this._resetCSSrelative( this._elWrap.style );
		this._resetCSSrelative( this._elHandlers.style );
		if ( x_ || y_ ) {
			this.setPosition( x + x_, y + y_ );
			this.__x = this._x;
			this.__y = this._y;
		}
	}
	_onmousemoveHandler( dir, e ) {
		const fnResize = this.onresizing,
			x = e.clientX - this._mousedownX,
			y = e.clientY - this._mousedownY;

		this._pos.x = x;
		this._pos.y = y;
		this._mousemoveX = x;
		this._mousemoveY = y;
		this._calcCSSrelativeResize( dir, this._pos );
		this._setCSSrelativeResize( this._elHandlers.style, dir, this._pos );
		if ( !this.parent._lowGraphics ) {
			this._setCSSrelativeResize( this._elWrap.style, dir, this._pos );
			if ( fnResize ) {
				const w = this._w,
					h = this._h - this._mousedownHeadHeight;

				switch ( dir ) {
					case "n":  fnResize( w,     h - y ); break;
					case "w":  fnResize( w - x, h     ); break;
					case "e":  fnResize( w + x, h     ); break;
					case "s":  fnResize( w,     h + y ); break;
					case "nw": fnResize( w - x, h - y ); break;
					case "ne": fnResize( w + x, h - y ); break;
					case "sw": fnResize( w - x, h + y ); break;
					case "se": fnResize( w + x, h + y ); break;
				}
			}
		}
	}
	_onmouseupHandler( dir, e ) {
		const x = this._x,
			y = this._y,
			w = this._w,
			h = this._h,
			p = this._pos;

		this._setClass( "dragging", false );
		this._resetCSSrelative( this._elWrap.style );
		this._resetCSSrelative( this._elHandlers.style );
		if ( p.x || p.y ) {
			switch ( dir ) {
				case "e" : this.setSize( w + p.x, h       ); break;
				case "se": this.setSize( w + p.x, h + p.y ); break;
				case "s" : this.setSize( w,       h + p.y ); break;
				case "sw": this.setSize( w - p.x, h + p.y ); this.setPosition( x + p.x, y       ); break;
				case "w" : this.setSize( w - p.x, h       ); this.setPosition( x + p.x, y       ); break;
				case "nw": this.setSize( w - p.x, h - p.y ); this.setPosition( x + p.x, y + p.y ); break;
				case "n" : this.setSize( w,       h - p.y ); this.setPosition( x,       y + p.y ); break;
				case "ne": this.setSize( w + p.x, h - p.y ); this.setPosition( x,       y + p.y ); break;
			}
		}
	}

	// private:
	_getElem( c ) {
		return this.rootElement.querySelector( `.gsuiWindow-${ c }` );
	}
	_attachTo( parentElem ) {
		parentElem.append( this.rootElement );
	}
	_setClass( clazz, b ) {
		this.rootElement.classList.toggle( `gsuiWindow-${ clazz }`, b );
	}
	_setZIndex( z ) {
		this.zIndex =
		this.rootElement.style.zIndex = z;
	}
	_callOnresize() {
		if ( this.onresize ) {
			const bcr = this._getElem( "content" ).getBoundingClientRect();

			this.onresize( bcr.width, bcr.height );
		}
	}
	_getHeadHeight() {
		return this._getElem( "head" ).getBoundingClientRect().height;
	}
	_resetCSSrelative( st ) {
		st.top =
		st.left =
		st.right =
		st.bottom = 0;
	}
	_setCSSrelativeMove( st, x, y ) {
		st.top    = `${  y }px`;
		st.left   = `${  x }px`;
		st.right  = `${ -x }px`;
		st.bottom = `${ -y }px`;
	}
	_calcCSSrelativeResize( dir, p ) {
		const w = this._w - 32,
			h = this._h - this._mousedownHeadHeight - 32;

		switch ( dir ) {
			case "n" : if ( h - p.y < 0 ) { p.y =  h; } break;
			case "s" : if ( h + p.y < 0 ) { p.y = -h; } break;
			case "w" :                                  if ( w - p.x < 0 ) { p.x =  w; } break;
			case "e" :                                  if ( w + p.x < 0 ) { p.x = -w; } break;
			case "nw": if ( h - p.y < 0 ) { p.y =  h; } if ( w - p.x < 0 ) { p.x =  w; } break;
			case "ne": if ( h - p.y < 0 ) { p.y =  h; } if ( w + p.x < 0 ) { p.x = -w; } break;
			case "sw": if ( h + p.y < 0 ) { p.y = -h; } if ( w - p.x < 0 ) { p.x =  w; } break;
			case "se": if ( h + p.y < 0 ) { p.y = -h; } if ( w + p.x < 0 ) { p.x = -w; } break;
		}
	}
	_setCSSrelativeResize( st, dir, p ) {
		switch ( dir ) {
			case "n" : st.top    = `${  p.y }px`; break;
			case "s" : st.bottom = `${ -p.y }px`; break;
			case "w" : st.left   = `${  p.x }px`; break;
			case "e" : st.right  = `${ -p.x }px`; break;
			case "nw": st.left   = `${  p.x }px`; st.top    = `${  p.y }px`; break;
			case "ne": st.right  = `${ -p.x }px`; st.top    = `${  p.y }px`; break;
			case "sw": st.left   = `${  p.x }px`; st.bottom = `${ -p.y }px`; break;
			case "se": st.right  = `${ -p.x }px`; st.bottom = `${ -p.y }px`; break;
		}
	}
}

gsuiWindow.template = document.querySelector( "#gsuiWindow-template" );
gsuiWindow.template.remove();
gsuiWindow.template.removeAttribute( "id" );

Object.freeze( gsuiWindow );
<commit_msg>gsuiWindows: setMinSize()<commit_after>"use strict";

class gsuiWindow {
	constructor( parent, id ) {
		const root = gsuiWindow.template.cloneNode( true );

		this.id = id;
		this.parent = parent;
		this.rootElement = root;
		this._elWrap = this._getElem( "wrap" );
		this._elHandlers = this._getElem( "handlers" );
		this._show =
		this._minimized =
		this._maximized = false;
		this.zIndex = 0;
		this.onresize =
		this.onfocusin =
		this.onresizing = null;
		this._pos = Object.seal( { x: 0, y: 0 } );
		this._mousedownX =
		this._mousedownY =
		this._mousemoveX =
		this._mousemoveY =
		this._mousedownHeadHeight = 0;
		this._x =
		this._y =
		this._w =
		this._h =
		this.__x =
		this.__y =
		this.__w =
		this.__h =
		this._wMin =
		this._hMin = 32;
		Object.seal( this );

		root.dataset.windowId = id;
		root.addEventListener( "focusin", parent._onfocusinWin.bind( parent, this ) );
		this._getElem( "icon" ).ondblclick = this.close.bind( this );
		this._getElem( "headBtns" ).onclick = this._onclickBtns.bind( this );
		this._getElem( "head" ).onmousedown = this._onmousedownHead.bind( this );
		this._getElem( "title" ).ondblclick =
		this._getElem( "headContent" ).ondblclick = this._ondblclickTitle.bind( this );
		this._elHandlers.onmousedown = this._onmousedownHandlers.bind( this );
		this._setZIndex( 0 );
		this.setTitle( id );
		this.setPosition( 0, 0 );
		this.setSize( 300, 150 );
	}

	open() { return this.openToggle( true ); }
	close() { return this.openToggle( false ); }
	openToggle( b ) {
		if ( b !== this._show ) {
			if ( b ) {
				this._show = true;
				this._setClass( "show", true );
				this.parent._open( this );
			} else if ( !this.onclose || this.onclose() !== false ) {
				this._show = false;
				this._setClass( "show", false );
				this.parent._close( this );
			}
		}
	}

	setIdAttr( id ) {
		this.rootElement.id = id;
	}
	setTitleIcon( icon ) {
		this._getElem( "icon" ).dataset.icon = icon;
	}
	empty() {
		const cnt = this._getElem( "content" ),
			headCnt = this._getElem( "headContent" );

		while ( cnt.lastChild ) {
			cnt.lastChild.remove();
		}
		while ( headCnt.lastChild ) {
			headCnt.lastChild.remove();
		}
	}
	append( ...args ) {
		this._getElem( "content" ).append( ...args );
	}
	headAppend( ...args ) {
		this._getElem( "headContent" ).append( ...args );
	}

	focus() {
		const root = this.rootElement;

		if ( !root.contains( document.activeElement ) ) {
			setTimeout( root.focus.bind( root ), 50 );
		}
	}
	maximize() {
		if ( !this._maximized ) {
			const st = this.rootElement.style;

			this.__x = this._x;
			this.__y = this._y;
			this.__w = this._w;
			if ( !this._minimized ) {
				this.__h = this._h;
			}
			st.top = st.left = st.right = st.bottom = st.width = st.height = "";
			this._setClass( "maximized", true );
			this._setClass( "minimized", false );
			this._maximized = true;
			this._minimized = false;
			this._callOnresize();
			this.focus();
			this.parent._winMaximized( this.id );
		}
	}
	minimize() {
		if ( !this._minimized ) {
			if ( !this._maximized ) {
				this.__w = this._w;
				this.__h = this._h;
				this.__x = this._x;
				this.__y = this._y;
			}
			this._setClass( "minimized", true );
			this._setClass( "maximized", false );
			this._minimized = true;
			this._maximized = false;
			this.setSize( this.__w, this._getHeadHeight(), "nocallback" );
			this.setPosition( this.__x, this.__y );
			this.parent._winRestored( this.id );
		}
	}
	restore() {
		if ( this._minimized || this._maximized ) {
			this.focus();
			this._setClass( "minimized", false );
			this._setClass( "maximized", false );
			this._minimized =
			this._maximized = false;
			this.setSize( this.__w, this.__h );
			this.setPosition( this.__x, this.__y );
			this.parent._winRestored( this.id );
		}
	}

	movable( b ) {
		this._setClass( "movable", b );
	}
	setTitle( t ) {
		this._getElem( "title" ).textContent = t;
	}
	setSize( w, h, nocb ) {
		this._w = w;
		this._h = h;
		this.rootElement.style.width = `${ w }px`;
		this.rootElement.style.height = `${ h }px`;
		if ( nocb !== "nocallback" ) {
			this._callOnresize();
		}
	}
	setMinSize( w, h ) {
		this._wMin = w;
		this._hMin = h;
	}
	setPosition( x, y ) {
		this._x = x;
		this._y = y;
		this.rootElement.style.left = `${ x }px`;
		this.rootElement.style.top = `${ y }px`;
	}

	// events:
	_onclickBtns( e ) {
		const act = e.target.dataset.icon;

		if ( act ) {
			this[ act ]();
		}
	}
	_ondblclickTitle( e ) {
		if ( e.target === e.currentTarget ) {
			this._maximized
				? this.restore()
				: this.maximize();
		}
	}
	_onmousedownHead( e ) {
		const clTar = e.target.classList,
			clicked =
				clTar.contains( "gsuiWindow-head" ) ||
				clTar.contains( "gsuiWindow-title" ) ||
				clTar.contains( "gsuiWindow-headContent" );

		if ( clicked ) {
			this._mousedownX = e.clientX;
			this._mousedownY = e.clientY;
			this._setClass( "dragging", true );
			this.parent._startMousemoving( "move",
				this._onmousemoveHead.bind( this ),
				this._onmouseupHead.bind( this ) );
		}
	}
	_onmousedownHandlers( e ) {
		const dir = e.target.dataset.dir;

		if ( dir ) {
			this._mousedownX = e.clientX;
			this._mousedownY = e.clientY;
			this._mousedownHeadHeight = this._getHeadHeight();
			this._setClass( "dragging", true );
			this.parent._startMousemoving( `${ dir }-resize`,
				this._onmousemoveHandler.bind( this, dir ),
				this._onmouseupHandler.bind( this, dir ) );
		}
	}
	_onmousemoveHead( e ) {
		const x = e.clientX - this._mousedownX,
			y = e.clientY - this._mousedownY;

		this._mousemoveX = x;
		this._mousemoveY = y;
		this._setCSSrelativeMove( this._elHandlers.style, x, y );
		if ( !this.parent._lowGraphics ) {
			this._setCSSrelativeMove( this._elWrap.style, x, y );
		}
	}
	_onmouseupHead( e ) {
		const x = this._x,
			y = this._y,
			x_ = e.clientX - this._mousedownX,
			y_ = e.clientY - this._mousedownY;

		this._setClass( "dragging", false );
		this._resetCSSrelative( this._elWrap.style );
		this._resetCSSrelative( this._elHandlers.style );
		if ( x_ || y_ ) {
			this.setPosition( x + x_, y + y_ );
			this.__x = this._x;
			this.__y = this._y;
		}
	}
	_onmousemoveHandler( dir, e ) {
		const fnResize = this.onresizing,
			x = e.clientX - this._mousedownX,
			y = e.clientY - this._mousedownY;

		this._pos.x = x;
		this._pos.y = y;
		this._mousemoveX = x;
		this._mousemoveY = y;
		this._calcCSSrelativeResize( dir, this._pos );
		this._setCSSrelativeResize( this._elHandlers.style, dir, this._pos );
		if ( !this.parent._lowGraphics ) {
			this._setCSSrelativeResize( this._elWrap.style, dir, this._pos );
			if ( fnResize ) {
				const w = this._w,
					h = this._h - this._mousedownHeadHeight;

				switch ( dir ) {
					case "n":  fnResize( w,     h - y ); break;
					case "w":  fnResize( w - x, h     ); break;
					case "e":  fnResize( w + x, h     ); break;
					case "s":  fnResize( w,     h + y ); break;
					case "nw": fnResize( w - x, h - y ); break;
					case "ne": fnResize( w + x, h - y ); break;
					case "sw": fnResize( w - x, h + y ); break;
					case "se": fnResize( w + x, h + y ); break;
				}
			}
		}
	}
	_onmouseupHandler( dir, e ) {
		const x = this._x,
			y = this._y,
			w = this._w,
			h = this._h,
			p = this._pos;

		this._setClass( "dragging", false );
		this._resetCSSrelative( this._elWrap.style );
		this._resetCSSrelative( this._elHandlers.style );
		if ( p.x || p.y ) {
			switch ( dir ) {
				case "e" : this.setSize( w + p.x, h       ); break;
				case "se": this.setSize( w + p.x, h + p.y ); break;
				case "s" : this.setSize( w,       h + p.y ); break;
				case "sw": this.setSize( w - p.x, h + p.y ); this.setPosition( x + p.x, y       ); break;
				case "w" : this.setSize( w - p.x, h       ); this.setPosition( x + p.x, y       ); break;
				case "nw": this.setSize( w - p.x, h - p.y ); this.setPosition( x + p.x, y + p.y ); break;
				case "n" : this.setSize( w,       h - p.y ); this.setPosition( x,       y + p.y ); break;
				case "ne": this.setSize( w + p.x, h - p.y ); this.setPosition( x,       y + p.y ); break;
			}
		}
	}

	// private:
	_getElem( c ) {
		return this.rootElement.querySelector( `.gsuiWindow-${ c }` );
	}
	_attachTo( parentElem ) {
		parentElem.append( this.rootElement );
	}
	_setClass( clazz, b ) {
		this.rootElement.classList.toggle( `gsuiWindow-${ clazz }`, b );
	}
	_setZIndex( z ) {
		this.zIndex =
		this.rootElement.style.zIndex = z;
	}
	_callOnresize() {
		if ( this.onresize ) {
			const bcr = this._getElem( "content" ).getBoundingClientRect();

			this.onresize( bcr.width, bcr.height );
		}
	}
	_getHeadHeight() {
		return this._getElem( "head" ).getBoundingClientRect().height;
	}
	_resetCSSrelative( st ) {
		st.top =
		st.left =
		st.right =
		st.bottom = 0;
	}
	_setCSSrelativeMove( st, x, y ) {
		st.top    = `${  y }px`;
		st.left   = `${  x }px`;
		st.right  = `${ -x }px`;
		st.bottom = `${ -y }px`;
	}
	_calcCSSrelativeResize( dir, p ) {
		const w = this._w - this._wMin,
			h = this._h - this._mousedownHeadHeight - this._hMin;

		switch ( dir ) {
			case "n" : if ( h - p.y < 0 ) { p.y =  h; } break;
			case "s" : if ( h + p.y < 0 ) { p.y = -h; } break;
			case "w" :                                  if ( w - p.x < 0 ) { p.x =  w; } break;
			case "e" :                                  if ( w + p.x < 0 ) { p.x = -w; } break;
			case "nw": if ( h - p.y < 0 ) { p.y =  h; } if ( w - p.x < 0 ) { p.x =  w; } break;
			case "ne": if ( h - p.y < 0 ) { p.y =  h; } if ( w + p.x < 0 ) { p.x = -w; } break;
			case "sw": if ( h + p.y < 0 ) { p.y = -h; } if ( w - p.x < 0 ) { p.x =  w; } break;
			case "se": if ( h + p.y < 0 ) { p.y = -h; } if ( w + p.x < 0 ) { p.x = -w; } break;
		}
	}
	_setCSSrelativeResize( st, dir, p ) {
		switch ( dir ) {
			case "n" : st.top    = `${  p.y }px`; break;
			case "s" : st.bottom = `${ -p.y }px`; break;
			case "w" : st.left   = `${  p.x }px`; break;
			case "e" : st.right  = `${ -p.x }px`; break;
			case "nw": st.left   = `${  p.x }px`; st.top    = `${  p.y }px`; break;
			case "ne": st.right  = `${ -p.x }px`; st.top    = `${  p.y }px`; break;
			case "sw": st.left   = `${  p.x }px`; st.bottom = `${ -p.y }px`; break;
			case "se": st.right  = `${ -p.x }px`; st.bottom = `${ -p.y }px`; break;
		}
	}
}

gsuiWindow.template = document.querySelector( "#gsuiWindow-template" );
gsuiWindow.template.remove();
gsuiWindow.template.removeAttribute( "id" );

Object.freeze( gsuiWindow );
<|endoftext|>