<commit_before>define(['jquery', 'base/js/dialog', 'base/js/namespace'],
function ($, dialog, Jupyter) {
    'use strict';

    var NS = 'anaconda-cloud',
      THUMBNAIL_MIN_DIM = 48,
      LOGO_URL = 'http://binstar-static-prod.s3.amazonaws.com' +
        '/latest/img/AnacondaCloud_logo_green.png';

    function api(route){
      // reusable API mapping, handles
      return Jupyter.notebook.base_url + NS + '/' + route;
    }

    function metadata(key, value){
        // wrapper for metadata, ensuring the top-level namespace
        var md = Jupyter.notebook.metadata;

        // ensure top-level namespace is initialized
        md[NS] = md[NS] || {};

        if (!arguments.length) {
          return md[NS];
        } else if(arguments.length === 1){
          return md[NS][key];
        } else{
          md[NS][key] = value;
        }
    }

    function publishNotebook(){
        metadata('environment', metadata('attach-environment') ?
            Jupyter.notebook.kernel.name :
            null);
        Jupyter.notebook.save_notebook().then(uploadNotebook);
    }

    function uploadNotebook() {
        var interval;

        $.ajax({
              url: api('publish'),
              method: 'POST',
              dataType: 'json',
              contentType: 'application/json; charset=utf-8',
              processData: false,
              data: JSON.stringify({
                  name: Jupyter.notebook.notebook_name,
                  content: Jupyter.notebook.toJSON()
              })
          })
          .done(function(data) {
              Jupyter.notification_area.get_widget('notebook')
                  .set_message(Jupyter.notebook.notebook_name +
                      ' was published',
                      4000);
              metadata('url', data.url);
              Jupyter.notebook.save_notebook();
              updateVisitLink();
          })
          .fail(showError)
          .always(function(data, textStatus) {
              clearInterval(interval);
          });
        interval = uploadingNotification();
    }

    function visitNotebook() {
        var url = metadata('url');
        if (url) {
            var _window = window.open(url, '_blank');
            if (_window) {
                _window.focus();
            }
        }
    }

    function uploadingNotification() {
        var index = 0,
            pattern = ['-', '\\', '|', '/'],
            _updateString = function(i) {
                Jupyter.notification_area.
                    get_widget('notebook').
                    warning('Uploading ' + pattern[i]);
            }
        _updateString(index);
        return setInterval(function() {
            index+=1;
            if (index > 3) { index = 0 };
            _updateString(index);
        }, 250);
    }

    function updateVisitLink(){
        if (!metadata('url')) {
            $('#visit_notebook').addClass('hide');
        } else {
            $('#visit_notebook').removeClass('hide');
        }
    }

    function thumbnailable(){
        // find good candidate thumbnails
        return $('#notebook')
          .find('img, canvas, svg')
          .filter(function(){
            return this.clientWidth > THUMBNAIL_MIN_DIM &&
              this.clientHeight > THUMBNAIL_MIN_DIM;
          });
    }

    function configureUpload(){
        var url,
            body,
            form,
            name,
            modal,
            attach,
            select,
            summary,
            adminUrl,
            thumbnail,
            thumbnails;

        Jupyter.notification_area.get_widget('notebook')
            .set_message('Connecting to Anaconda Cloud', 2000);

        $.ajax({
            url: api('login'),
            method: 'GET',
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
        }).done(function(data) {
            body = $('<div/>');
            name = IPython.notebook.notebook_name.replace(/.ipynb$/, '');

            // avoid any surprises with user events firing in the background
            Jupyter.notebook.keyboard_manager.register_events(body);

            url = metadata('url') || '';
            adminUrl = !url ? '' :
              url.replace('https://notebooks.', 'https://') + '/settings/admin';

            // actually build the form
            form = $('<div/>', {
                'class': 'form-horizontal',
                'role': 'form'
            }).appendTo(body);

            // provide prior publishing information
            if(url){
              $('<blockquote/>').append(
                  $('<span/>')
                      .text('This notebook was previously published as '),
                  $('<a/>', {href: url, target: '_blank'}).text(url)
              )
              .css({'border-left': 'solid 4px #43B02A'})
              .appendTo(body);
            }

            // warning about published status
            $('<p/>')
                .text('Initially, all published notebooks will be ').append(
                  $('<label/>', {'class': 'label label-default'})
                    .text('public'),
                  $('<span/>').text('. ')
                )
            .appendTo(body);

            // call to action to change settings
            if(adminUrl){
                $('<blockquote/>').append(
                    $('<span/>').text('Make '),
                    $('<strong/>').text(name),
                    $('<span/>').text(' '),
                    $('<label/>', {'class': 'label label-primary'})
                        .text('private'),
                    $('<span/>').text(' or '),
                    $('<label/>', {'class': 'label label-success'})
                        .text('authenticated'),
                    $('<span/>').text(' on the '),
                    $('<a/>', {href: adminUrl, target: '_blank'})
                        .text('Anaconda Cloud settings page'),
                    $('<span/>').text('.'))
                .appendTo(body);
            }

            // organization picker, if needed
            if (data.organizations && data.organizations.length) {
                select = $('<select/>', {
                        id: 'select-organization',
                        'class': 'form-control'
                    })

                $('<div/>', {'class': 'form-group'}).append(
                    $('<label/>', {'class': 'control-label col-sm-3'})
                        .text('User/Organization'),
                    $('<div/>', {'class': 'col-sm-9'}).append(select))
                .appendTo(form);

                [data.user].concat(data.organizations)
                    .forEach(function(org) {
                        $('<option/>', {value: org.login})
                            .text(org.login + ' (' + org.name + ')')
                            .appendTo(select);
                    });

                if (metadata('organization')) {
                    select.val(metadata('organization'));
                }
            }

            summary = $('<input/>', {
                'class': 'form-control',
                type: 'text',
                id: 'anaconda-summary',
                placeholder: 'A brief summary'
            })
            .val(metadata('summary'))
            .on('input', function(){
                metadata('summary', summary.val())
            });

            $('<div/>', {'class': 'form-group'}).append(
                $('<label/>', {'class': 'control-label col-sm-3'})
                    .text('Summary'),
                $('<div/>', {'class': 'col-sm-9'}).append(summary))
            .appendTo(form);

            // whether to include the environment in metadata
            attach = $('<input/>', {type: 'checkbox'}).prop({
                checked: metadata('attach-environment')})
                .on("change", function(){
                  metadata('attach-environment', attach.prop('checked'));
                });

            $('<div/>', {'class': 'form-group'}).append(
                $('<label/>', {'class': 'control-label col-sm-3'})
                    .text('Environment'),
                $('<div/>', {'class': 'col-sm-9'}).append(
                    $('<div/>', {'class': 'checkbox'}).append(
                        $('<label/>').append(attach,
                            $('<span/>').text('Attach conda environment')))))
            .appendTo(form);

            // thumbnails
            thumbnail = $('<img/>', {src: metadata('thumbnail'), height: 72});

            thumbnails = [];

            thumbnailable().map(function(){
                var canvas,
                  ctx,
                  uri;

                switch(this.tagName.toLowerCase()){
                  case 'canvas':
                    canvas = this;
                    uri = canvas.toDataURL();
                    break;
                  case 'img':
                    canvas = $('<canvas/>').css({display: 'none'});
                    ctx = canvas[0].getContext('2d');
                    ctx.canvas.width = this.clientWidth;
                    ctx.canvas.height = this.clientHeight;
                    ctx.drawImage(this, 0, 0);
                    uri = canvas[0].toDataURL();
                    canvas.remove();
                    break;
                }

                thumbnails.push($('<li/>', {value: uri}).append(
                    $('<a/>')
                        .on('click', function(){
                            metadata('thumbnail', uri);
                            thumbnail.attr({src: uri})
                        })
                        .append($('<img/>', {src: uri, height: 100}))));
            });

            if(thumbnails.length){
              $('<div/>', {'class': 'form-group'}).append(
                  $('<label/>', {'class': 'control-label col-sm-3'})
                      .text('Thumbnail'),
                  $('<div/>', {'class': 'col-sm-9'}).append(
                      $('<div/>', {'class': 'dropdown'}).append(
                          $('<button/>', {
                              'class': 'btn btn-default dropdown-toggle',
                              'type': 'button',
                              'data-toggle': 'dropdown'
                          }).append(thumbnail),
                          $('<ul/>', {'class': 'dropdown-menu'})
                            .append(thumbnails)
                  )))
              .appendTo(form);
            }

            $('<div/>', {'class': 'form-group'}).append(
                $('<div/>', {'class': 'col-sm-9 col-sm-offset-3'}).append(
                    $('<button/>', {'class': 'btn btn-lg btn-success'})
                      .on('click', function(){
                        modal.modal('hide');
                        publishNotebook();
                      })
                      .append(
                        $('<i/>', {'class': 'fa fa-cloud-upload'}),
                        $('<span/>').text(' Publish')
                      )))
            .appendTo(form);

            // revert notebook CSS customizations to bootstrap default
            form.find('.control-label label')
                .css({'font-weight': 'bold'});
            form.find('.form-control')
                .css({margin: 0});
            form.find('.col-sm-3, .col-sm-9')
                .css({'padding-left': '15px', 'padding-right': '15px'});

            modal = dialog.modal({
                body: body,
                buttons: {
                    'Close': {}
                }
            });

            modal.find('.modal-title').append(
                $('<span/>').text('Publish '),
                $('<strong/>')
                  .text(name),
                $('<span/>').text(' to '),
                $('<a/>', {href: 'https://anaconda.org', target: '_blank'})
                  .append($('<img/>', {src: LOGO_URL, height: 32}))
            );

        }).fail(showError);
    }

    function showError(jqXHR, textStatus, label) {
        var notif, title, body;

        console.log(arguments)

        switch(jqXHR.status){
          case 401:
            title = 'Unauthorized';
            notif = [
              $('<p>').text('You are not authorized to complete this action.' +
                  ' You may need to run this at the command line:'),
              $('<pre>').text('anaconda login')
            ];
            break;
          case 500:
            title = label;
            notif = [
              $('<p>').text('An error ocurred in the Notebook Server'),
              $('<p>').text('You may need to re-install nb_anacondacloud')
            ];
            break;
          default:
            title = 'Error: ' + label;
            notif = [
              $('<p>').text('An error ocurred in the Notebook Server'),
            ]
        }

        Jupyter.notification_area.get_widget('notebook').
            danger(title, 4000);

        body = $('<div>').append(notif);

        dialog.modal({
            title: title,
            body: body,
            buttons : {
                OK: {}
            }
        });
    }

    function updateToolbar() {
        if (!Jupyter.toolbar) {
            $([Jupyter.events])
              .on('app_initialized.NotebookApp', updateToolbar);
            return;
        }
        if ($('#publish_notebook').length === 0) {
            Jupyter.toolbar.add_buttons_group([{
                label: 'Publish your notebook into Anaconda.org',
                icon: 'fa-cloud-upload',
                callback: configureUpload,
                id: 'publish_notebook'
            }, {
                label: 'Visit your notebook',
                icon: 'fa-cloud',
                callback: visitNotebook,
                id: 'visit_notebook'
            }]);
        }
        updateVisitLink();
    }

    function load_jupyter_extension() {
        updateToolbar();
    }

    // export new and old API entrypoints
    return {
      load_ipython_extension: load_jupyter_extension,
      load_jupyter_extension: load_jupyter_extension,
    };
});
<commit_msg>removing deprecated namespace: IPython -> Jupyter<commit_after>define(['jquery', 'base/js/dialog', 'base/js/namespace'],
function ($, dialog, Jupyter) {
    'use strict';

    var NS = 'anaconda-cloud',
      THUMBNAIL_MIN_DIM = 48,
      LOGO_URL = 'http://binstar-static-prod.s3.amazonaws.com' +
        '/latest/img/AnacondaCloud_logo_green.png';

    function api(route){
      // reusable API mapping, handles
      return Jupyter.notebook.base_url + NS + '/' + route;
    }

    function metadata(key, value){
        // wrapper for metadata, ensuring the top-level namespace
        var md = Jupyter.notebook.metadata;

        // ensure top-level namespace is initialized
        md[NS] = md[NS] || {};

        if (!arguments.length) {
          return md[NS];
        } else if(arguments.length === 1){
          return md[NS][key];
        } else{
          md[NS][key] = value;
        }
    }

    function publishNotebook(){
        metadata('environment', metadata('attach-environment') ?
            Jupyter.notebook.kernel.name :
            null);
        Jupyter.notebook.save_notebook().then(uploadNotebook);
    }

    function uploadNotebook() {
        var interval;

        $.ajax({
              url: api('publish'),
              method: 'POST',
              dataType: 'json',
              contentType: 'application/json; charset=utf-8',
              processData: false,
              data: JSON.stringify({
                  name: Jupyter.notebook.notebook_name,
                  content: Jupyter.notebook.toJSON()
              })
          })
          .done(function(data) {
              Jupyter.notification_area.get_widget('notebook')
                  .set_message(Jupyter.notebook.notebook_name +
                      ' was published',
                      4000);
              metadata('url', data.url);
              Jupyter.notebook.save_notebook();
              updateVisitLink();
          })
          .fail(showError)
          .always(function(data, textStatus) {
              clearInterval(interval);
          });
        interval = uploadingNotification();
    }

    function visitNotebook() {
        var url = metadata('url');
        if (url) {
            var _window = window.open(url, '_blank');
            if (_window) {
                _window.focus();
            }
        }
    }

    function uploadingNotification() {
        var index = 0,
            pattern = ['-', '\\', '|', '/'],
            _updateString = function(i) {
                Jupyter.notification_area.
                    get_widget('notebook').
                    warning('Uploading ' + pattern[i]);
            }
        _updateString(index);
        return setInterval(function() {
            index+=1;
            if (index > 3) { index = 0 };
            _updateString(index);
        }, 250);
    }

    function updateVisitLink(){
        if (!metadata('url')) {
            $('#visit_notebook').addClass('hide');
        } else {
            $('#visit_notebook').removeClass('hide');
        }
    }

    function thumbnailable(){
        // find good candidate thumbnails
        return $('#notebook')
          .find('img, canvas, svg')
          .filter(function(){
            return this.clientWidth > THUMBNAIL_MIN_DIM &&
              this.clientHeight > THUMBNAIL_MIN_DIM;
          });
    }

    function configureUpload(){
        var url,
            body,
            form,
            name,
            modal,
            attach,
            select,
            summary,
            adminUrl,
            thumbnail,
            thumbnails;

        Jupyter.notification_area.get_widget('notebook')
            .set_message('Connecting to Anaconda Cloud', 2000);

        $.ajax({
            url: api('login'),
            method: 'GET',
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
        }).done(function(data) {
            body = $('<div/>');
            name = Jupyter.notebook.notebook_name.replace(/.ipynb$/, '');

            // avoid any surprises with user events firing in the background
            Jupyter.notebook.keyboard_manager.register_events(body);

            url = metadata('url') || '';
            adminUrl = !url ? '' :
              url.replace('https://notebooks.', 'https://') + '/settings/admin';

            // actually build the form
            form = $('<div/>', {
                'class': 'form-horizontal',
                'role': 'form'
            }).appendTo(body);

            // provide prior publishing information
            if(url){
              $('<blockquote/>').append(
                  $('<span/>')
                      .text('This notebook was previously published as '),
                  $('<a/>', {href: url, target: '_blank'}).text(url)
              )
              .css({'border-left': 'solid 4px #43B02A'})
              .appendTo(body);
            }

            // warning about published status
            $('<p/>')
                .text('Initially, all published notebooks will be ').append(
                  $('<label/>', {'class': 'label label-default'})
                    .text('public'),
                  $('<span/>').text('. ')
                )
            .appendTo(body);

            // call to action to change settings
            if(adminUrl){
                $('<blockquote/>').append(
                    $('<span/>').text('Make '),
                    $('<strong/>').text(name),
                    $('<span/>').text(' '),
                    $('<label/>', {'class': 'label label-primary'})
                        .text('private'),
                    $('<span/>').text(' or '),
                    $('<label/>', {'class': 'label label-success'})
                        .text('authenticated'),
                    $('<span/>').text(' on the '),
                    $('<a/>', {href: adminUrl, target: '_blank'})
                        .text('Anaconda Cloud settings page'),
                    $('<span/>').text('.'))
                .appendTo(body);
            }

            // organization picker, if needed
            if (data.organizations && data.organizations.length) {
                select = $('<select/>', {
                        id: 'select-organization',
                        'class': 'form-control'
                    })

                $('<div/>', {'class': 'form-group'}).append(
                    $('<label/>', {'class': 'control-label col-sm-3'})
                        .text('User/Organization'),
                    $('<div/>', {'class': 'col-sm-9'}).append(select))
                .appendTo(form);

                [data.user].concat(data.organizations)
                    .forEach(function(org) {
                        $('<option/>', {value: org.login})
                            .text(org.login + ' (' + org.name + ')')
                            .appendTo(select);
                    });

                if (metadata('organization')) {
                    select.val(metadata('organization'));
                }
            }

            summary = $('<input/>', {
                'class': 'form-control',
                type: 'text',
                id: 'anaconda-summary',
                placeholder: 'A brief summary'
            })
            .val(metadata('summary'))
            .on('input', function(){
                metadata('summary', summary.val())
            });

            $('<div/>', {'class': 'form-group'}).append(
                $('<label/>', {'class': 'control-label col-sm-3'})
                    .text('Summary'),
                $('<div/>', {'class': 'col-sm-9'}).append(summary))
            .appendTo(form);

            // whether to include the environment in metadata
            attach = $('<input/>', {type: 'checkbox'}).prop({
                checked: metadata('attach-environment')})
                .on("change", function(){
                  metadata('attach-environment', attach.prop('checked'));
                });

            $('<div/>', {'class': 'form-group'}).append(
                $('<label/>', {'class': 'control-label col-sm-3'})
                    .text('Environment'),
                $('<div/>', {'class': 'col-sm-9'}).append(
                    $('<div/>', {'class': 'checkbox'}).append(
                        $('<label/>').append(attach,
                            $('<span/>').text('Attach conda environment')))))
            .appendTo(form);

            // thumbnails
            thumbnail = $('<img/>', {src: metadata('thumbnail'), height: 72});

            thumbnails = [];

            thumbnailable().map(function(){
                var canvas,
                  ctx,
                  uri;

                switch(this.tagName.toLowerCase()){
                  case 'canvas':
                    canvas = this;
                    uri = canvas.toDataURL();
                    break;
                  case 'img':
                    canvas = $('<canvas/>').css({display: 'none'});
                    ctx = canvas[0].getContext('2d');
                    ctx.canvas.width = this.clientWidth;
                    ctx.canvas.height = this.clientHeight;
                    ctx.drawImage(this, 0, 0);
                    uri = canvas[0].toDataURL();
                    canvas.remove();
                    break;
                }

                thumbnails.push($('<li/>', {value: uri}).append(
                    $('<a/>')
                        .on('click', function(){
                            metadata('thumbnail', uri);
                            thumbnail.attr({src: uri})
                        })
                        .append($('<img/>', {src: uri, height: 100}))));
            });

            if(thumbnails.length){
              $('<div/>', {'class': 'form-group'}).append(
                  $('<label/>', {'class': 'control-label col-sm-3'})
                      .text('Thumbnail'),
                  $('<div/>', {'class': 'col-sm-9'}).append(
                      $('<div/>', {'class': 'dropdown'}).append(
                          $('<button/>', {
                              'class': 'btn btn-default dropdown-toggle',
                              'type': 'button',
                              'data-toggle': 'dropdown'
                          }).append(thumbnail),
                          $('<ul/>', {'class': 'dropdown-menu'})
                            .append(thumbnails)
                  )))
              .appendTo(form);
            }

            $('<div/>', {'class': 'form-group'}).append(
                $('<div/>', {'class': 'col-sm-9 col-sm-offset-3'}).append(
                    $('<button/>', {'class': 'btn btn-lg btn-success'})
                      .on('click', function(){
                        modal.modal('hide');
                        publishNotebook();
                      })
                      .append(
                        $('<i/>', {'class': 'fa fa-cloud-upload'}),
                        $('<span/>').text(' Publish')
                      )))
            .appendTo(form);

            // revert notebook CSS customizations to bootstrap default
            form.find('.control-label label')
                .css({'font-weight': 'bold'});
            form.find('.form-control')
                .css({margin: 0});
            form.find('.col-sm-3, .col-sm-9')
                .css({'padding-left': '15px', 'padding-right': '15px'});

            modal = dialog.modal({
                body: body,
                buttons: {
                    'Close': {}
                }
            });

            modal.find('.modal-title').append(
                $('<span/>').text('Publish '),
                $('<strong/>')
                  .text(name),
                $('<span/>').text(' to '),
                $('<a/>', {href: 'https://anaconda.org', target: '_blank'})
                  .append($('<img/>', {src: LOGO_URL, height: 32}))
            );

        }).fail(showError);
    }

    function showError(jqXHR, textStatus, label) {
        var notif, title, body;

        console.log(arguments)

        switch(jqXHR.status){
          case 401:
            title = 'Unauthorized';
            notif = [
              $('<p>').text('You are not authorized to complete this action.' +
                  ' You may need to run this at the command line:'),
              $('<pre>').text('anaconda login')
            ];
            break;
          case 500:
            title = label;
            notif = [
              $('<p>').text('An error ocurred in the Notebook Server'),
              $('<p>').text('You may need to re-install nb_anacondacloud')
            ];
            break;
          default:
            title = 'Error: ' + label;
            notif = [
              $('<p>').text('An error ocurred in the Notebook Server'),
            ]
        }

        Jupyter.notification_area.get_widget('notebook').
            danger(title, 4000);

        body = $('<div>').append(notif);

        dialog.modal({
            title: title,
            body: body,
            buttons : {
                OK: {}
            }
        });
    }

    function updateToolbar() {
        if (!Jupyter.toolbar) {
            $([Jupyter.events])
              .on('app_initialized.NotebookApp', updateToolbar);
            return;
        }
        if ($('#publish_notebook').length === 0) {
            Jupyter.toolbar.add_buttons_group([{
                label: 'Publish your notebook into Anaconda.org',
                icon: 'fa-cloud-upload',
                callback: configureUpload,
                id: 'publish_notebook'
            }, {
                label: 'Visit your notebook',
                icon: 'fa-cloud',
                callback: visitNotebook,
                id: 'visit_notebook'
            }]);
        }
        updateVisitLink();
    }

    function load_jupyter_extension() {
        updateToolbar();
    }

    // export new and old API entrypoints
    return {
      load_ipython_extension: load_jupyter_extension,
      load_jupyter_extension: load_jupyter_extension,
    };
});
<|endoftext|>


<commit_before>import {LitElement, css, html} from 'lit';
import {ref, createRef} from 'lit/directives/ref.js';
import './chromedash-roadmap';
import {showToastMessage} from './utils';
import {SHARED_STYLES} from '../sass/shared-css.js';

export class ChromedashRoadmapPage extends LitElement {
  sectionRef = createRef();
  roadmapRef = createRef();

  static get styles() {
    return [
      ...SHARED_STYLES,
      css`
        .animate {
          transition: margin .4s ease;
        }

        #releases-section {
          overflow: hidden;
        }

        .timeline-controls {
          text-align: center;
          padding-bottom: var(--content-padding);
        }
    `];
  }

  static get properties() {
    return {
      user: {type: Object},
      cardWidth: {type: Number},
      numColumns: {type: Number},
      viewOffset: {type: Number},
    };
  }

  constructor() {
    super();
    this.user = {};
    this.cardWidth = 0;
    this.numColumns = 0;
    this.viewOffset = 0;
  }

  connectedCallback() {
    super.connectedCallback();
    this.fetchData();
    window.addEventListener('resize', () => this.handleResize());
  }

  disconnectedCallback() {
    window.removeEventListener('resize', () => this.handleResize());
    super.disconnectedCallback();
  }

  firstUpdated() {
    this.handleResize();
  }

  fetchData() {
    window.csClient.getPermissions().then((user) => {
      this.user = user;

      // TODO(kevinshen56714): Remove this once SPA index page is set up.
      // Has to include this for now to remove the spinner at _base.html.
      document.body.classList.remove('loading');
    }).catch(() => {
      showToastMessage('Some errors occurred. Please refresh the page or try again later.');
    });
  }

  handleResize() {
    const containerWidth = this.sectionRef.value.offsetWidth;
    const margin = 16;

    let numColumns = 3;
    if (window.matchMedia('(max-width: 768px)').matches) {
      numColumns = 1;
    } else if (window.matchMedia('(max-width: 1152px)').matches) {
      numColumns = 2;
    }
    this.numColumns = numColumns;
    this.cardWidth = (containerWidth/numColumns) - margin;

    this.updateRoadmapMargin(false);
  };

  handleMove(e) {
    const roadmap = this.roadmapRef.value;
    if (!roadmap.lastFutureFetchedOn) return;

    if (e.target.id == 'next-button') {
      this.viewOffset -= 1; // move to newer version
      roadmap.lastMilestoneVisible += 1;
      this.updateRoadmapMargin(true);
    } else {
      this.viewOffset += 1; // move to older version
      roadmap.lastMilestoneVisible -= 1;
      this.updateRoadmapMargin(true);
    }

    // Fetch when second last card is displayed
    if (roadmap.lastMilestoneVisible - roadmap.lastFutureFetchedOn > 1) {
      roadmap.fetchNextBatch(roadmap.lastFutureFetchedOn + 1);
    } else if (roadmap.lastPastFetchedOn - roadmap.lastMilestoneVisible == 0) {
      roadmap.fetchPreviousBatch(roadmap.lastPastFetchedOn - 1);
    }
  }

  updateRoadmapMargin(animated) {
    const roadmap = this.roadmapRef.value;
    if (animated) {
      roadmap.classList.add('animate');
    } else {
      roadmap.classList.remove('animate');
    }
    const margin = 16;
    roadmap.style.marginLeft = this.viewOffset*(this.cardWidth + margin) + 'px';
    roadmap.style.left = roadmap.cardOffset*(this.cardWidth + margin) + 'px';
  }

  render() {
    return html`

Roadmap

          <button id="previous-button" aria-label="Button to move to previous release" @click=${this.handleMove}>Previous</button>
          <button id="next-button" aria-label="Button to move to later release" @click=${this.handleMove}>Next</button>

        <chromedash-roadmap
          ${ref(this.roadmapRef)}
          class="animate"
          aria-label="List of milestone releases"
          .numColumns=${this.numColumns}
          .cardWidth=${this.cardWidth}
          ?signedIn=${Boolean(this.user)}>
        </chromedash-roadmap>

    `;
  }
}

customElements.define('chromedash-roadmap-page', ChromedashRoadmapPage);
<commit_msg>Fix roadmap fetch bug in small views (#2065)<commit_after>import {LitElement, css, html} from 'lit';
import {ref, createRef} from 'lit/directives/ref.js';
import './chromedash-roadmap';
import {showToastMessage} from './utils';
import {SHARED_STYLES} from '../sass/shared-css.js';

export class ChromedashRoadmapPage extends LitElement {
  sectionRef = createRef();
  roadmapRef = createRef();

  static get styles() {
    return [
      ...SHARED_STYLES,
      css`
        .animate {
          transition: margin .4s ease;
        }

        #releases-section {
          overflow: hidden;
        }

        .timeline-controls {
          text-align: center;
          padding-bottom: var(--content-padding);
        }
    `];
  }

  static get properties() {
    return {
      user: {type: Object},
      cardWidth: {type: Number},
      numColumns: {type: Number},
      viewOffset: {type: Number},
    };
  }

  constructor() {
    super();
    this.user = {};
    this.cardWidth = 0;
    this.numColumns = 0;
    this.viewOffset = 0;
  }

  connectedCallback() {
    super.connectedCallback();
    this.fetchData();
    window.addEventListener('resize', () => this.handleResize());
  }

  disconnectedCallback() {
    window.removeEventListener('resize', () => this.handleResize());
    super.disconnectedCallback();
  }

  firstUpdated() {
    this.handleResize();
  }

  fetchData() {
    window.csClient.getPermissions().then((user) => {
      this.user = user;

      // TODO(kevinshen56714): Remove this once SPA index page is set up.
      // Has to include this for now to remove the spinner at _base.html.
      document.body.classList.remove('loading');
    }).catch(() => {
      showToastMessage('Some errors occurred. Please refresh the page or try again later.');
    });
  }

  handleResize() {
    const containerWidth = this.sectionRef.value.offsetWidth;
    const margin = 16;

    let numColumns = 3;
    if (window.matchMedia('(max-width: 768px)').matches) {
      numColumns = 1;
    } else if (window.matchMedia('(max-width: 1152px)').matches) {
      numColumns = 2;
    }
    this.numColumns = numColumns;
    this.cardWidth = (containerWidth/numColumns) - margin;

    this.updateRoadmapMargin(false);
  };

  handleMove(e) {
    const roadmap = this.roadmapRef.value;
    if (!roadmap.lastFutureFetchedOn) return;

    if (e.target.id == 'next-button') {
      this.viewOffset -= 1; // move to newer version
      roadmap.lastMilestoneVisible += 1;
      this.updateRoadmapMargin(true);
    } else {
      this.viewOffset += 1; // move to older version
      roadmap.lastMilestoneVisible -= 1;
      this.updateRoadmapMargin(true);
    }

    // Fetch when second last card is displayed
    if (roadmap.lastMilestoneVisible - roadmap.lastFutureFetchedOn > 1) {
      roadmap.fetchNextBatch(roadmap.lastFutureFetchedOn + 1);
    } else if (roadmap.lastMilestoneVisible - roadmap.lastPastFetchedOn <= 0) {
      roadmap.fetchPreviousBatch(roadmap.lastPastFetchedOn - 1);
    }
  }

  updateRoadmapMargin(animated) {
    const roadmap = this.roadmapRef.value;
    if (animated) {
      roadmap.classList.add('animate');
    } else {
      roadmap.classList.remove('animate');
    }
    const margin = 16;
    roadmap.style.marginLeft = this.viewOffset*(this.cardWidth + margin) + 'px';
    roadmap.style.left = roadmap.cardOffset*(this.cardWidth + margin) + 'px';
  }

  render() {
    return html`

Roadmap

          <button id="previous-button" aria-label="Button to move to previous release" @click=${this.handleMove}>Previous</button>
          <button id="next-button" aria-label="Button to move to later release" @click=${this.handleMove}>Next</button>

        <chromedash-roadmap
          ${ref(this.roadmapRef)}
          class="animate"
          aria-label="List of milestone releases"
          .numColumns=${this.numColumns}
          .cardWidth=${this.cardWidth}
          ?signedIn=${Boolean(this.user)}>
        </chromedash-roadmap>

    `;
  }
}

customElements.define('chromedash-roadmap-page', ChromedashRoadmapPage);
<|endoftext|>


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

define(['jquery', 'd3', 'd3tip', 'd3textwrap', 'vizhelpers', 'underscore'],
    function ($, d3, d3tip, d3textwrap, vizhelpers, _) {

        // The samples in our data, bucketed by their corresponding
        // bar graph value
        var sampleSet = {};

        // The currently selected values on the bar graph, corresponding to the buckets
        // in the sampleSet
        var selectedValues = {};

        // The samples found in the selected value buckets; this is used to produce the JSON which
        // is submitted by the form
        var selectedSamples = null;
        var selex_active = false;
        var zoom_status = {
            translation: null,
            scale: null
        };

        return {

            dataCounts: function (data, x_attr, bySample) {
                var val_set = {};
                var results = [];

                data.map(function(d){
                    var val = d[x_attr];
                    var sample_id = d['sample_id'];
                    var case_id = d['case_id'];
                    if (!val_set[val]) {
                        val_set[val] = {};
                    }

                    if(bySample){
                        val_set[val][sample_id] = 1;
                    }
                    else if(!val_set[val][case_id]){
                        val_set[val][case_id] = 1
                    }
                    if (!sampleSet[val]) {
                        sampleSet[val] = {};
                    }
                    sampleSet[val]['{' + sample_id + '}{' + case_id + '}'] = {
                        sample: sample_id,
                        case: case_id,
                        project: d['project']
                    };
                });

                for (var key in val_set) {
                    results.push({'value': key, 'count': Object.keys(val_set[key]).length});
                }

                return results;
            },
            createBarGraph: function (svg, raw_Data, width, height, bar_width, x_attr, xLabel, margin, bySample) {
                // If you want to override the tip coming in from the create call,
                // do it here
                var tip = d3tip()
                    .attr('class', 'd3-tip')
                    .direction('n')
                    .offset([0, 0])
                    .html(function (d) {
                        return d.value + ': ' + d.count + (bySample ? ' sample' : ' case') + (d.count > 1 ? 's':'');
                    });

                var data = this.dataCounts(raw_Data, x_attr, bySample);
                var plot_width = (bar_width + 5) * data.length;
                var view_plot_width = plot_width < width - margin.left - margin.right ? width - margin.left - margin.right : plot_width;

                if (width < plot_width + margin.left + margin.right) {
                    svg.attr('width', plot_width + margin.left + margin.right);
                }

                var x = d3.scale.ordinal()
                    .domain(data.map(function (d) {
                        return d.value;
                    }))
                    .rangeBands([0, plot_width], .1);
                var xAxis = d3.svg.axis()
                    .scale(x)
                    .orient('bottom');
                var y = d3.scale.linear()
                    .range([height - margin.bottom - margin.top, 0])
                    .domain([0, d3.max(data, function (d) {
                        return d.count;
                    })])
                    .nice();
                var yAxis = d3.svg.axis()
                    .scale(y)
                    .orient('left')
                    .tickSize(-view_plot_width, 0, 0);
                var zoomer = function () {
                    if (!selex_active) {
                        svg.select('.x.axis')
                            .attr('transform', 'translate(' + (d3.event.translate[0] + margin.left) + ',' + (height - margin.bottom) + ')')
                            .call(xAxis.scale(x.rangeBands([0, plot_width * d3.event.scale], 0.1)));
                        plot_area.selectAll('.plot-bar').attr('transform', 'translate(' + d3.event.translate[0] + ',0) scale(' + d3.event.scale + ', 1)');
                        x3.range([0, plot_width*d3.event.scale]);
                    }
                };

                var x2 = d3.scale.linear()
                    .range([0, view_plot_width])
                    .domain([0, view_plot_width]);
                var x3 = d3.scale.linear()
                    .range([0, plot_width])
                    .domain([0, plot_width]);

                var min_scale = 18 / bar_width;
                var max_scale = 1.5;
                var zoom = d3.behavior.zoom()
                    .x(x2)
                    .scaleExtent([min_scale, max_scale])
                    .on('zoom', zoomer);

                svg.call(zoom);
                var worksheet_id = $('.worksheet.active').attr('id');
                var plot_area_clip_id = 'plot_area_clip_' + worksheet_id;
                var plot_area = svg.append('g')
                    .attr('clip-path', 'url(#' + plot_area_clip_id + ')')
                    .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

                plot_area.append('clipPath')
                    .attr('id', plot_area_clip_id)
                    .append('rect')
                    .attr({
                        width: view_plot_width,
                        height: height - margin.top - margin.bottom
                    });

                plot_area.selectAll(".plot-bar")
                    .data(data)
                    .enter().append("rect")
                    .attr("class", "plot-bar")
                    .attr("x", function (d) {
                        return x(d.value);
                    })
                    .attr("y", function (d) {
                        return y(d.count);
                    })
                    .attr('value', function (d) {
                        return d.value;
                    })
                    .attr("width", x.rangeBand())
                    .attr("height", function (d) {
                        return height - margin.bottom - margin.top - y(d.count);
                    })
                    .on('mouseover.tip', tip.show)
                    .on('mouseout.tip', tip.hide);

                var x_axis_area_clip_id = 'x_axis_area_clip_' + worksheet_id;
                var x_axis_area = svg.append('g')
                    .attr('clip-path', 'url(#' + x_axis_area_clip_id + ')');

                x_axis_area.append('clipPath')
                    .attr('id', x_axis_area_clip_id)
                    .append('rect')
                    .attr('height', margin.bottom)
                    .attr('width', view_plot_width)
                    .attr('transform', 'translate(' + margin.left + ',' + (height - margin.bottom) + ')');
                x_axis_area.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(' + margin.left + ',' + (height - margin.bottom) + ')')
                    .call(xAxis);

                var x_axis_area_height = margin.bottom - 55;
                svg.select('.x.axis').selectAll('text').each(function () {
                    var d = d3.select(this);
                    var label = d.text();
                    var parent = d3.select(d.node().parentNode);
                    d.remove();
                    var fOb = parent.append('foreignObject')
                        .attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
                        .attr('width', x_axis_area_height)
                        .attr('height', bar_width);

                    fOb.append('xhtml:div')
                        .style('height', bar_width)
                        .style('width', x_axis_area_height)
                        .attr('class', 'truncated-single')
                        .attr('title', label)
                        .html(label);
                });

                svg.select('.x.axis').selectAll('foreignObject').attr('style', 'transform: translate(-15px,' + (x_axis_area_height + 10) + 'px) rotate(-90deg);');

                // Highlight the selected rectangles whenever the cursor is moved
                var brushmove = function (p) {
                    var e = brush.extent();
                    var reCalc = false;
                    var oldSet = selectedValues;
                    selectedValues = {};
                    svg.selectAll('rect.plot-bar').classed("selected", function (d) {
                        return x3(e[0]) <= x($(this).attr('value')) + $(this).attr('width') / 2
                            && (x($(this).attr('value')) + $(this).attr('width') / 2) <= x3(e[1]);
                    });

                    if (Object.keys(oldSet).length !== $('.worksheet.active rect.plot-bar.selected').length) {
                        reCalc = true;
                    }

                    $('.worksheet.active rect.plot-bar.selected').each(function () {
                        if (!oldSet[$(this).attr('value')]) {
                            reCalc = true;
                        }
                        selectedValues[$(this).attr('value')] = 1;
                    });

                    var oldSetKeys = Object.keys(oldSet);
                    for (var i = 0; i < oldSetKeys.length && !reCalc; i++) {
                        if (!selectedValues[oldSet[oldSetKeys[i]]]) {
                            reCalc = true;
                        }
                    }

                    sample_form_update(e, reCalc);
                };

                // If the brush is empty, select all circles.
                var brushend = function () {
                    if (brush.empty()) {
                        svg.selectAll(".hidden").classed("hidden", false);
                        $('.worksheet.active .save-cohort-card').hide();
                    }
                };

                var brush = d3.svg.brush()
                    .x(x2)
                    .on('brushstart', function (d) {
                        var e = brush.extent();
                        if(!e){
                            selectedValues = {};
                            selectedSamples = null;
                            sample_form_update(e, true);
                        }
                    })
                    .on('brush', brushmove)
                    .on('brushend', brushend);

                svg.call(tip);

                // append axes
                svg.append('g')
                    .attr('class', 'y axis')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
                    .call(yAxis);

                // append axes labels
                svg.append('g')
                    .attr('class', 'x-label-container')
                    .append('text')
                    .attr('class', 'x label axis-label')
                    .attr('text-anchor', 'middle')
                    .attr('transform', 'translate(' + (view_plot_width / 2) + ',' + (height - 10) + ')')
                    .text(xLabel);

                svg.append('text')
                    .attr('class', 'y label axis-label')
                    .attr('text-anchor', 'middle')
                    .text('Number of '+ (bySample ? 'Samples' : 'Cases'))
                    .attr('transform', 'rotate(-90) translate(' + (-height - margin.top + margin.bottom) / 2 + ', 20)');

                var check_selection_state = function (obj) {
                    selex_active = !!obj;
                    if (obj) {
                        // Disable zooming events and store their status
                        svg.on('.zoom', null);
                        zoom_status.translation = zoom.translate();
                        // Append new brush event listeners to plot area only
                        plot_area.append('g')
                            .attr('class', 'brush')
                            .call(brush)
                            .selectAll('rect')
                            .attr('y', 0)
                            .attr('height', height - margin.bottom)
                            .attr('transform', 'translate(0, 0)');
                    } else {
                        // Resume zooming, restoring the zoom's last state
                        svg.call(zoom);
                        zoom_status.translation && zoom.translate(zoom_status.translation);
                        zoom_status.translation = null;

                        // Clear selections
                        $('.worksheet.active .plot').find('.selected-samples-count').html('Number of Samples: ' + 0);
                        $('.worksheet.active .plot').find('.selected-patients-count').html('Number of Cases: ' + 0);
                        $('.worksheet.active .save-cohort-form input[name="samples"]').attr('value', "");
                        svg.selectAll('.selected').classed('selected', false);
                        $('.worksheet.active .save-cohort-card').hide();
                        selectedValues = {};
                        selectedSamples = null;

                        // Remove brush event listener plot area - comment out if we want to enable selection carry-over
                        brush.clear();
                        plot_area.selectAll('.brush').remove();
                    }
                };

                //Update the sample cohort bar update
                function sample_form_update(extent, reCalc) {
                    if (reCalc) {
                        var case_set = {};
                        selectedSamples = {};
                        _.each(Object.keys(selectedValues), function (val) {
                            _.each(Object.keys(sampleSet[val]), function (sample) {
                                selectedSamples[sample] = sampleSet[val][sample];
                                case_set[sampleSet[val][sample]['case']] = 1;
                            });
                        });
                        $('.worksheet.active .plot').find('.selected-samples-count').html('Number of Samples: ' + Object.keys(selectedSamples).length);
                        $('.worksheet.active .plot').find('.selected-patients-count').html('Number of Cases: ' + Object.keys(case_set).length);
                        $('.worksheet.active .save-cohort-card').find('.btn').prop('disabled', (Object.keys(selectedSamples).length <= 0));
                    }
                    if (extent) {
                        var leftVal = Math.min((x3(extent[1]) + 20), (width - $('.worksheet.active .save-cohort-card').width()));
                        $('.worksheet.active .save-cohort-card').show()
                            .attr('style', 'position:relative; top: -' + height + 'px; left:' + leftVal + 'px;');
                    }
                }

                function resize() {
                    width = svg.node().parentNode.offsetWidth - 10;
                    //TODO resize plot
                }

                function check_selection_state_wrapper(bool) {
                    check_selection_state(bool);
                }

                $('.worksheet.active .save-cohort-card').find('.btn').on('click', function (e) {
                    if (Object.keys(selectedValues).length > 0) {
                        var selected_sample_set = [];
                        _.each(Object.keys(selectedSamples), function (sample) {
                            selected_sample_set.push(selectedSamples[sample]);
                        });
                        $('.worksheet.active .save-cohort-form input[name="samples"]').attr('value', JSON.stringify(selected_sample_set));
                    }
                });

                function get_json_data() {
                    var p_data = {};
                    data.map(function (d, i) {
                        p_data[i] = d;
                    });
                    return p_data;
                }

                function get_csv_data() {
                    var csv_data = "value, count\n";
                    data.map(function (d) {
                        csv_data += d['value'] + ', ' + d['count'] + '\n';
                    });
                    return csv_data;
                }

                return {
                    get_json: get_json_data,
                    get_csv: get_csv_data,
                    resize: resize,
                    check_selection_state: check_selection_state_wrapper
                }
            }
        };
    });
<commit_msg>bugfix<commit_after>/**
 *
 * Copyright 2016, Institute for Systems Biology
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

define(['jquery', 'd3', 'd3tip', 'd3textwrap', 'vizhelpers', 'underscore'],
    function ($, d3, d3tip, d3textwrap, vizhelpers, _) {

        // The samples in our data, bucketed by their corresponding
        // bar graph value
        var sampleSet = {};

        // The currently selected values on the bar graph, corresponding to the buckets
        // in the sampleSet
        var selectedValues = {};

        // The samples found in the selected value buckets; this is used to produce the JSON which
        // is submitted by the form
        var selectedSamples = null;
        var selex_active = false;
        var zoom_status = {
            translation: null,
            scale: null
        };

        return {

            dataCounts: function (data, x_attr, bySample) {
                var val_set = {};
                var results = [];

                data.map(function(d){
                    var val = d[x_attr];
                    var sample_id = d['sample_id'];
                    var case_id = d['case_id'];
                    if (!val_set[val]) {
                        val_set[val] = {};
                    }

                    if(bySample){
                        val_set[val][sample_id] = 1;
                    }
                    else if(!val_set[val][case_id]){
                        val_set[val][case_id] = 1
                    }
                    if (!sampleSet[val]) {
                        sampleSet[val] = {};
                    }
                    sampleSet[val]['{' + sample_id + '}{' + case_id + '}'] = {
                        sample: sample_id,
                        case: case_id,
                        project: d['project']
                    };
                });

                for (var key in val_set) {
                    results.push({'value': key, 'count': Object.keys(val_set[key]).length});
                }

                return results;
            },
            createBarGraph: function (svg, raw_Data, width, height, bar_width, x_attr, xLabel, margin, bySample) {
                // If you want to override the tip coming in from the create call,
                // do it here
                var tip = d3tip()
                    .attr('class', 'd3-tip')
                    .direction('n')
                    .offset([0, 0])
                    .html(function (d) {
                        return d.value + ': ' + d.count + (bySample ? ' sample' : ' case') + (d.count > 1 ? 's':'');
                    });

                var data = this.dataCounts(raw_Data, x_attr, bySample);
                var plot_width = (bar_width + 5) * data.length;
                var view_plot_width = plot_width < width - margin.left - margin.right ? width - margin.left - margin.right : plot_width;

                if (width < plot_width + margin.left + margin.right) {
                    svg.attr('width', plot_width + margin.left + margin.right);
                }

                var x = d3.scale.ordinal()
                    .domain(data.map(function (d) {
                        return d.value;
                    }))
                    .rangeBands([0, plot_width], .1);
                var xAxis = d3.svg.axis()
                    .scale(x)
                    .orient('bottom');
                var y = d3.scale.linear()
                    .range([height - margin.bottom - margin.top, 0])
                    .domain([0, d3.max(data, function (d) {
                        return d.count;
                    })])
                    .nice();
                var yAxis = d3.svg.axis()
                    .scale(y)
                    .orient('left')
                    .tickSize(-view_plot_width, 0, 0);
                var zoomer = function () {
                    if (!selex_active) {
                        svg.select('.x.axis')
                            .attr('transform', 'translate(' + (d3.event.translate[0] + margin.left) + ',' + (height - margin.bottom) + ')')
                            .call(xAxis.scale(x.rangeBands([0, plot_width * d3.event.scale], 0.1)));
                        plot_area.selectAll('.plot-bar').attr('transform', 'translate(' + d3.event.translate[0] + ',0) scale(' + d3.event.scale + ', 1)');
                        x3.range([0, plot_width*d3.event.scale]);
                    }
                };

                var x2 = d3.scale.linear()
                    .range([0, view_plot_width])
                    .domain([0, view_plot_width]);
                var x3 = d3.scale.linear()
                    .range([0, plot_width])
                    .domain([0, plot_width]);

                var min_scale = 18 / bar_width;
                var max_scale = 1.5;
                var zoom = d3.behavior.zoom()
                    .x(x2)
                    .scaleExtent([min_scale, max_scale])
                    .on('zoom', zoomer);

                svg.call(zoom);
                var worksheet_id = $('.worksheet.active').attr('id');
                var plot_area_clip_id = 'plot_area_clip_' + worksheet_id;
                var plot_area = svg.append('g')
                    .attr('clip-path', 'url(#' + plot_area_clip_id + ')')
                    .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');

                plot_area.append('clipPath')
                    .attr('id', plot_area_clip_id)
                    .append('rect')
                    .attr({
                        width: view_plot_width,
                        height: height - margin.top - margin.bottom
                    });

                plot_area.selectAll(".plot-bar")
                    .data(data)
                    .enter().append("rect")
                    .attr("class", "plot-bar")
                    .attr("x", function (d) {
                        return x(d.value);
                    })
                    .attr("y", function (d) {
                        return y(d.count);
                    })
                    .attr('value', function (d) {
                        return d.value;
                    })
                    .attr("width", x.rangeBand())
                    .attr("height", function (d) {
                        return height - margin.bottom - margin.top - y(d.count);
                    })
                    .on('mouseover.tip', tip.show)
                    .on('mouseout.tip', tip.hide);

                var x_axis_area_clip_id = 'x_axis_area_clip_' + worksheet_id;
                var x_axis_area = svg.append('g')
                    .attr('clip-path', 'url(#' + x_axis_area_clip_id + ')');

                x_axis_area.append('clipPath')
                    .attr('id', x_axis_area_clip_id)
                    .append('rect')
                    .attr('height', margin.bottom)
                    .attr('width', view_plot_width)
                    .attr('transform', 'translate(' + margin.left + ',' + (height - margin.bottom) + ')');
                x_axis_area.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(' + margin.left + ',' + (height - margin.bottom) + ')')
                    .call(xAxis);

                var x_axis_area_height = margin.bottom - 55;
                svg.select('.x.axis').selectAll('text').each(function () {
                    var d = d3.select(this);
                    var label = d.text();
                    var parent = d3.select(d.node().parentNode);
                    d.remove();
                    var fOb = parent.append('foreignObject')
                        .attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
                        .attr('width', x_axis_area_height)
                        .attr('height', bar_width);

                    fOb.append('xhtml:div')
                        .style('height', bar_width)
                        .style('width', x_axis_area_height)
                        .attr('class', 'truncated-single')
                        .attr('title', label)
                        .html(label);
                });

                svg.select('.x.axis').selectAll('foreignObject').attr('style', 'transform: translate(-15px,' + (x_axis_area_height + 10) + 'px) rotate(-90deg);');

                // Highlight the selected rectangles whenever the cursor is moved
                var brushmove = function (p) {
                    var e = brush.extent();
                    var reCalc = false;
                    var oldSet = selectedValues;
                    selectedValues = {};
                    svg.selectAll('rect.plot-bar').classed("selected", function (d) {
                        return x3(e[0]) <= x($(this).attr('value')) + $(this).attr('width') / 2
                            && (x($(this).attr('value')) + $(this).attr('width') / 2) <= x3(e[1]);
                    });

                    if (Object.keys(oldSet).length !== $('.worksheet.active rect.plot-bar.selected').length) {
                        reCalc = true;
                    }

                    $('.worksheet.active rect.plot-bar.selected').each(function () {
                        if (!oldSet[$(this).attr('value')]) {
                            reCalc = true;
                        }
                        selectedValues[$(this).attr('value')] = 1;
                    });

                    var oldSetKeys = Object.keys(oldSet);
                    for (var i = 0; i < oldSetKeys.length && !reCalc; i++) {
                        if (!selectedValues[oldSet[oldSetKeys[i]]]) {
                            reCalc = true;
                        }
                    }

                    sample_form_update(e, reCalc);
                };

                // If the brush is empty, select all circles.
                var brushend = function () {
                    if (brush.empty()) {
                        svg.selectAll(".hidden").classed("hidden", false);
                        $('.worksheet.active .save-cohort-card').hide();
                    }
                };

                var brush = d3.svg.brush()
                    .x(x2)
                    .on('brushstart', function (d) {
                        var e = brush.extent();
                        if(!e){
                            selectedValues = {};
                            selectedSamples = null;
                            sample_form_update(e, true);
                        }
                    })
                    .on('brush', brushmove)
                    .on('brushend', brushend);

                svg.call(tip);

                // append axes
                svg.append('g')
                    .attr('class', 'y axis')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
                    .call(yAxis);

                // append axes labels
                svg.append('g')
                    .attr('class', 'x-label-container')
                    .append('text')
                    .attr('class', 'x label axis-label')
                    .attr('text-anchor', 'middle')
                    .attr('transform', 'translate(' + (view_plot_width / 2) + ',' + (height - 10) + ')')
                    .text(xLabel);

                svg.append('text')
                    .attr('class', 'y label axis-label')
                    .attr('text-anchor', 'middle')
                    .text('Number of '+ (bySample ? 'Samples' : 'Cases'))
                    .attr('transform', 'rotate(-90) translate(' + (-height - margin.top + margin.bottom) / 2 + ', 20)');

                var check_selection_state = function (obj) {

                    selex_active = !!obj;

                    if (obj) {
                        // Disable zooming events and store their status
                        svg.on('.zoom', null);
                        zoom_status.translation = zoom.translate();
                        // Append new brush event listeners to plot area only
                        plot_area.append('g')
                            .attr('class', 'brush')
                            .call(brush)
                            .selectAll('rect')
                            .attr('y', 0)
                            .attr('height', height - margin.bottom)
                            .attr('transform', 'translate(0, 0)');
                    } else {
                        // Resume zooming, restoring the zoom's last state
                        svg.call(zoom);
                        zoom_status.translation && zoom.translate(zoom_status.translation);
                        zoom_status.translation = null;

                        // Clear selections
                        $('.worksheet.active .plot').find('.selected-samples-count').html('Number of Samples: ' + 0);
                        $('.worksheet.active .plot').find('.selected-patients-count').html('Number of Cases: ' + 0);
                        $('.worksheet.active .save-cohort-form input[name="samples"]').attr('value', "");
                        svg.selectAll('.selected').classed('selected', false);
                        $('.worksheet.active .save-cohort-card').hide();
                        selectedValues = {};
                        selectedSamples = null;
                        // Remove brush event listener plot area - comment out if we want to enable selection carry-over
                        brush.clear();
                        plot_area.selectAll('.brush').remove();
                    }
                };

                //Update the sample cohort bar update
                function sample_form_update(extent, reCalc) {

                    if (reCalc) {
                        var case_set = {};
                        selectedSamples = {};
                        _.each(Object.keys(selectedValues), function (val) {
                            _.each(Object.keys(sampleSet[val]), function (sample) {
                                selectedSamples[sample] = sampleSet[val][sample];
                                case_set[sampleSet[val][sample]['case']] = 1;
                            });
                        });

                        $('.worksheet.active .plot').find('.selected-samples-count').html('Number of Samples: ' + Object.keys(selectedSamples).length);
                        $('.worksheet.active .plot').find('.selected-patients-count').html('Number of Cases: ' + Object.keys(case_set).length);
                        $('.worksheet.active .save-cohort-card').find('.btn').prop('disabled', (Object.keys(selectedSamples).length <= 0));
                    }

                    if (extent) {
                        var leftVal = Math.min((x3(extent[1]) + 20), (width - $('.worksheet.active .save-cohort-card').width()));
                        $('.worksheet.active .save-cohort-card').show()
                            .attr('style', 'position:relative; top: -' + height + 'px; left:' + leftVal + 'px;');
                    }

                }

                function resize() {
                    width = svg.node().parentNode.offsetWidth - 10;
                    //TODO resize plot
                }

                function check_selection_state_wrapper(bool) {
                    check_selection_state(bool);
                }

                $('.worksheet.active .save-cohort-card').find('.btn').on('click', function (e) {
                    if (Object.keys(selectedValues).length > 0) {
                        var selected_sample_set = [];
                        _.each(Object.keys(selectedSamples), function (sample) {
                            selected_sample_set.push(selectedSamples[sample]);
                        });

                        $('.worksheet.active .save-cohort-form input[name="samples"]').attr('value', JSON.stringify(selected_sample_set));
                    }
                });

                function get_json_data() {
                    var p_data = {};
                    data.map(function (d, i) {
                        p_data[i] = d;
                    });
                    return p_data;
                }

                function get_csv_data() {
                    var csv_data = "value, count\n";
                    data.map(function (d) {
                        csv_data += d['value'] + ', ' + d['count'] + '\n';
                    });
                    return csv_data;
                }

                return {
                    get_json: get_json_data,
                    get_csv: get_csv_data,
                    resize: resize,
                    check_selection_state: check_selection_state_wrapper
                }
            }
        };
    });
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Skewer Everything
// @description  Add a toggle button to run Skewer on the current page
// @lastupdated  2013-01-24
// @version      1.0
// @license      Public Domain
// @include      http://*
// @require      http://code.jquery.com/jquery-latest.min.js
// ==/UserScript==

var toggle = $('<div/>').bind('click', inject).css({
    "width": '0px',
    "height": '0px',
    "border-style": 'solid',
    "border-width": '0 12px 12px 0',
    "border-color": 'transparent #F00 transparent transparent',
    "position": 'absolute',
    "right": 0,
    "top": 0,
    "z-index": 100
});

var injected = false;

function inject() {
    if (!injected) {
        var script = $('<script/>').attr({src: 'http://localhost:8080/skewer'});
        if (!unsafeWindow.jQuery) unsafeWindow.$ = $;
        $('head').append(script);
        toggle.css('border-right-color', '#0F0');
        injected = true;
    }
}

/* Don't use on iframes. */
if (window.top === window.self) {
    $('body').append(toggle);
}
<commit_msg>Remember the inject state between refreshes.<commit_after>// ==UserScript==
// @name         Skewer Everything
// @description  Add a toggle button to run Skewer on the current page
// @lastupdated  2013-01-24
// @version      1.0
// @license      Public Domain
// @include      http://*
// @require      http://code.jquery.com/jquery-latest.min.js
// ==/UserScript==

var toggle = $('<div/>').bind('click', inject).css({
    "width": '0px',
    "height": '0px',
    "border-style": 'solid',
    "border-width": '0 12px 12px 0',
    "border-color": 'transparent #F00 transparent transparent',
    "position": 'absolute',
    "right": 0,
    "top": 0,
    "z-index": 100
});

var injected = false;

function inject() {
    if (!injected) {
        var script = $('<script/>').attr({src: 'http://localhost:8080/skewer'});
        if (!unsafeWindow.jQuery) unsafeWindow.$ = $;
        $('head').append(script);
        toggle.css('border-right-color', '#0F0');
    } else {
        toggle.css('border-right-color', '#F00');
    }
    injected = !injected;
    GM_setValue('auto-' + location, injected);
}

/* Don't use on iframes. */
if (window.top === window.self) {
    $('body').append(toggle);
    if (GM_getValue('auto-' + location)) {
        inject();
    }
}
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

module.exports = function(options) {
  var module = {};
  var host;

  function ciHost() {
    if (!host) {
      var defaultHost = 'ci.p2devcloud.com';
      if (Math.random() <= 0.5) {
        defaultHost = 'ci2.p2devcloud.com'
      }

      host = options['ciHost'] || defaultHost;
    }

    return host;
  }

  /**
   * Determine if a given environment has been enabled by name.
   *
   * This function should only be used after options parameter is initialized from prompts.
   */
  function envActive(environment) {
    return options.environments.indexOf(environment) != -1;
  }

  function virtualHost(env, namespace) {
    namespace = env ? '-' + namespace : namespace;
    if (!env) {
      env = '';
    }

    return env + namespace + '.' + ciHost();
  };

  module.envActive = envActive;
  module.virtualHost = virtualHost;
  module.ciHost = ciHost;

  return module;
};
<commit_msg>Avoid unneccessary random number generation.<commit_after>'use strict';

var _ = require('lodash');

module.exports = function(options) {
  var module = {};
  var host;

  function ciHost() {
    if (!host) {
      host = options['ciHost'];
      if (!options['ciHost']) {
        var host = 'ci.p2devcloud.com';
        if (Math.random() <= 0.5) {
          host = 'ci2.p2devcloud.com'
        }
      }
    }

    return host;
  }

  /**
   * Determine if a given environment has been enabled by name.
   *
   * This function should only be used after options parameter is initialized from prompts.
   */
  function envActive(environment) {
    return options.environments.indexOf(environment) != -1;
  }

  function virtualHost(env, namespace) {
    namespace = env ? '-' + namespace : namespace;
    if (!env) {
      env = '';
    }

    return env + namespace + '.' + ciHost();
  };

  module.envActive = envActive;
  module.virtualHost = virtualHost;
  module.ciHost = ciHost;

  return module;
};
<|endoftext|>


<commit_before>/*
* Touch
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

(function(window) {

// TODO: support for double tap.
/**
 * Global utility for working with multi-touch enabled devices in EaselJS. Currently supports W3C Touch API
 * (iOS & modern Android browser) and IE10.
 * @class Touch
 * @static
 **/
var Touch = function() {
	throw "Touch cannot be instantiated";
}

// Public static methods:
	/**
	 * Returns true if touch is supported in the current browser.
	 * @method isSupported
	 * @return {Boolean} A boolean indicating whether touch is supported in the current browser.
	 * @static
	 **/
	Touch.isSupported = function() {
		return	('ontouchstart' in window) || // iOS
					(window.navigator['msPointerEnabled']); // IE10
	}

	/**
	 * Enables touch interaction for the specified EaselJS stage. Currently supports iOS (and compatible browsers, such
	 * as modern Android browsers), and IE10.
	 * Supports both single touch and multi-touch modes. Extends the EaselJS MouseEvent model, but without support for
	 * double click or over/out events. See MouseEvent.pointerID for more information.
	 * @method enable
	 * @param {Stage} stage The stage to enable touch on.
	 * @param {Boolean} singleTouch If true, only a single touch will be active at a time. Default is false.
	 * @param {Boolean} allowDefault If true, then default gesture actions (ex. scrolling, zooming) will be allowed when the user is interacting with the target canvas. Default is false.
	 * @return {Boolean} Returns true if touch was successfully enabled on the target stage.
	 * @static
	 **/
	Touch.enable = function(stage, singleTouch, allowDefault) {
		if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }

		// inject required properties on stage:
		stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};

		// note that in the future we may need to disable the standard mouse event model before adding
		// these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
		if ('ontouchstart' in window) { Touch._IOS_enable(stage); }
		else if (window.navigator['msPointerEnabled']) { Touch._IE_enable(stage); }
		return true;
	}

// Private static methods:

	/**
	 * @method _IOS_enable
	 * @protected
	 * @param {Stage} stage
	 **/
	Touch._IOS_enable = function(stage) {
		var canvas = stage.canvas;
		canvas.addEventListener("touchstart", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
		canvas.addEventListener("touchmove", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
		canvas.addEventListener("touchend", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
		canvas.addEventListener("touchcancel", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
	}

	/**
	 * @method _IOS_handleEvent
	 * @protected
	 **/
	Touch._IOS_handleEvent = function(stage, e) {
		if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); }
		var touches = e.changedTouches;
		var type = e.type;
		for (var i= 0,l=touches.length; i<l; i++) {
			var touch = touches[i];
			var id = touch.identifier;
			if (touch.target != stage.canvas) { continue; }

			if (type == "touchstart") {
				this._handleStart(stage, id, e, touch.pageX, touch.pageY);
			} else if (type == "touchmove") {
				this._handleMove(stage, id, e, touch.pageX, touch.pageY);
			} else if (type == "touchend" || type == "touchcancel") {
				this._handleEnd(stage, id, e);
			}
		}
	}

	/**
		 * @method _IE_enable
		 * @protected
		 * @param {Stage} stage
		 **/
		Touch._IE_enable = function(stage) {
			var canvas = stage.canvas;
			canvas.addEventListener("MSPointerDown", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			canvas.addEventListener("MSPointerMove", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			canvas.addEventListener("MSPointerUp", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			canvas.addEventListener("MSPointerCancel", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			if (stage.__touch.preventDefault) { canvas.style.msContentZooming = canvas.style.msTouchAction = "none"; }
		}

		/**
		 * @method _IE_handleEvent
		 * @protected
		 **/
		Touch._IE_handleEvent = function(stage, e) {
			if (stage.__touch.preventDefault) {
				e.preventDefault&&e.preventDefault();
				e.preventManipulation&&e.preventManipulation();
			}
			var type = e.type;
			var id = e.pointerId;
			if (e.srcElement != stage.canvas) { return; }

			if (type == "MSPointerDown") {
				this._handleStart(stage, id, e, e.pageX, e.pageY);
			} else if (type == "MSPointerMove") {
				this._handleMove(stage, id, e, e.pageX, e.pageY);
			} else if (type == "MSPointerUp" || type == "MSPointerCancel") {
				this._handleEnd(stage, id, e);
			}
		}

	/**
	 * @method _handleStart
	 * @protected
	 **/
	Touch._handleStart = function(stage, id, e, x, y) {
		var props = stage.__touch;
		if (!props.multitouch && props.count) { return; }
		var ids = props.pointers;
		if (ids[id]) { return; }
		ids[id] = true;
		props.count++;
		stage._handlePointerDown(id, e, x, y);
	}

	/**
	 * @method _handleMove
	 * @protected
	 **/
	Touch._handleMove = function(stage, id, e, x, y) {
		if (!stage.__touch.pointers[id]) { return; }
		stage._handlePointerMove(id, e, x, y);
	}

	/**
	 * @method _handleEnd
	 * @protected
	 **/
	Touch._handleEnd = function(stage, id, e) {
		// TODO: cancel should be handled differently for proper UI (ex. an up would trigger a click, a cancel would more closely resemble an out).
		var props = stage.__touch;
		var ids = props.pointers;
		if (!ids[id]) { return; }
		props.count--;
		stage._handlePointerUp(id, e, true);
		delete(ids[id]);
	}

window.Touch = Touch;
}(window));<commit_msg>Simplified how Touch preventDefault is handled on IE10.<commit_after>/*
* Touch
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

(function(window) {

// TODO: support for double tap.
/**
 * Global utility for working with multi-touch enabled devices in EaselJS. Currently supports W3C Touch API
 * (iOS & modern Android browser) and IE10.
 * @class Touch
 * @static
 **/
var Touch = function() {
	throw "Touch cannot be instantiated";
}

// Public static methods:
	/**
	 * Returns true if touch is supported in the current browser.
	 * @method isSupported
	 * @return {Boolean} A boolean indicating whether touch is supported in the current browser.
	 * @static
	 **/
	Touch.isSupported = function() {
		return	('ontouchstart' in window) || // iOS
					(window.navigator['msPointerEnabled']); // IE10
	}

	/**
	 * Enables touch interaction for the specified EaselJS stage. Currently supports iOS (and compatible browsers, such
	 * as modern Android browsers), and IE10.
	 * Supports both single touch and multi-touch modes. Extends the EaselJS MouseEvent model, but without support for
	 * double click or over/out events. See MouseEvent.pointerID for more information.
	 * @method enable
	 * @param {Stage} stage The stage to enable touch on.
	 * @param {Boolean} singleTouch If true, only a single touch will be active at a time. Default is false.
	 * @param {Boolean} allowDefault If true, then default gesture actions (ex. scrolling, zooming) will be allowed when the user is interacting with the target canvas. Default is false.
	 * @return {Boolean} Returns true if touch was successfully enabled on the target stage.
	 * @static
	 **/
	Touch.enable = function(stage, singleTouch, allowDefault) {
		if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }

		// inject required properties on stage:
		stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};

		// note that in the future we may need to disable the standard mouse event model before adding
		// these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
		if ('ontouchstart' in window) { Touch._IOS_enable(stage); }
		else if (window.navigator['msPointerEnabled']) { Touch._IE_enable(stage); }
		return true;
	}

// Private static methods:

	/**
	 * @method _IOS_enable
	 * @protected
	 * @param {Stage} stage
	 **/
	Touch._IOS_enable = function(stage) {
		var canvas = stage.canvas;
		canvas.addEventListener("touchstart", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
		canvas.addEventListener("touchmove", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
		canvas.addEventListener("touchend", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
		canvas.addEventListener("touchcancel", function(e) { Touch._IOS_handleEvent(stage,e); }, false);
	}

	/**
	 * @method _IOS_handleEvent
	 * @protected
	 **/
	Touch._IOS_handleEvent = function(stage, e) {
		if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); }
		var touches = e.changedTouches;
		var type = e.type;
		for (var i= 0,l=touches.length; i<l; i++) {
			var touch = touches[i];
			var id = touch.identifier;
			if (touch.target != stage.canvas) { continue; }

			if (type == "touchstart") {
				this._handleStart(stage, id, e, touch.pageX, touch.pageY);
			} else if (type == "touchmove") {
				this._handleMove(stage, id, e, touch.pageX, touch.pageY);
			} else if (type == "touchend" || type == "touchcancel") {
				this._handleEnd(stage, id, e);
			}
		}
	}

	/**
		 * @method _IE_enable
		 * @protected
		 * @param {Stage} stage
		 **/
		Touch._IE_enable = function(stage) {
			var canvas = stage.canvas;
			canvas.addEventListener("MSPointerDown", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			canvas.addEventListener("MSPointerMove", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			canvas.addEventListener("MSPointerUp", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			canvas.addEventListener("MSPointerCancel", function(e) { Touch._IE_handleEvent(stage,e); }, false);
			if (stage.__touch.preventDefault) { canvas.style.msTouchAction = "none"; }
		}

		/**
		 * @method _IE_handleEvent
		 * @protected
		 **/
		Touch._IE_handleEvent = function(stage, e) {
			if (stage.__touch.preventDefault) {
				e.preventDefault&&e.preventDefault();
			}
			var type = e.type;
			var id = e.pointerId;
			if (e.srcElement != stage.canvas) { return; }

			if (type == "MSPointerDown") {
				this._handleStart(stage, id, e, e.pageX, e.pageY);
			} else if (type == "MSPointerMove") {
				this._handleMove(stage, id, e, e.pageX, e.pageY);
			} else if (type == "MSPointerUp" || type == "MSPointerCancel") {
				this._handleEnd(stage, id, e);
			}
		}

	/**
	 * @method _handleStart
	 * @protected
	 **/
	Touch._handleStart = function(stage, id, e, x, y) {
		var props = stage.__touch;
		if (!props.multitouch && props.count) { return; }
		var ids = props.pointers;
		if (ids[id]) { return; }
		ids[id] = true;
		props.count++;
		stage._handlePointerDown(id, e, x, y);
	}

	/**
	 * @method _handleMove
	 * @protected
	 **/
	Touch._handleMove = function(stage, id, e, x, y) {
		if (!stage.__touch.pointers[id]) { return; }
		stage._handlePointerMove(id, e, x, y);
	}

	/**
	 * @method _handleEnd
	 * @protected
	 **/
	Touch._handleEnd = function(stage, id, e) {
		// TODO: cancel should be handled differently for proper UI (ex. an up would trigger a click, a cancel would more closely resemble an out).
		var props = stage.__touch;
		var ids = props.pointers;
		if (!ids[id]) { return; }
		props.count--;
		stage._handlePointerUp(id, e, true);
		delete(ids[id]);
	}

window.Touch = Touch;
}(window));<|endoftext|>