<commit_before>import _ from 'lodash';
import React from 'react';
import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable';

import { commitPhases } from './actions';
import { PLATFORM_TYPE, DRY_RUN } from './cluster-config';

import { CertificateAuthority } from './components/certificate-authority';
import { ClusterType } from './components/cluster-type';
import { DryRun } from './components/dry-run';
import { Etcd } from './components/etcd';
import { SubmitDefinition } from './components/submit-definition';
import { Success } from './components/success';
import { TF_PowerOn } from './components/tf-poweron';
import { Users } from './components/users';

import { BM_ClusterInfo } from './components/bm-cluster-info';
import { BM_Controllers, BM_Workers } from './components/bm-nodeforms';
import { BM_Credentials } from './components/bm-credentials';
import { BM_Hostname } from './components/bm-hostname';
import { BM_Matchbox } from './components/bm-matchbox';
import { BM_NetworkConfig } from './components/bm-network-config';
import { BM_SSHKeys } from './components/bm-sshkeys';

import { AWS_CloudCredentials } from './components/aws-cloud-credentials';
import { AWS_ClusterInfo } from './components/aws-cluster-info';
import { AWS_DefineNodes } from './components/aws-define-nodes';
import { AWS_SubmitKeys } from './components/aws-submit-keys';
import { AWS_VPC } from './components/aws-vpc';

import {
  AWS_TF,
  BARE_METAL_TF,
  isSupported,
} from './platforms';

// Common pages
const certificateAuthorityPage = {
  path: '/define/certificate-authority',
  component: CertificateAuthority,
  title: 'Certificate Authority',
};

const clusterTypePage = {
  path: '/define/cluster-type',
  component: ClusterType,
  title: 'Platform',
  hideSave: true,
  showRestore: true,
};

const dryRunPage = {
  path: '/define/advanced',
  component: DryRun,
  title: 'Download Assets',
};

const etcdPage = {
  path: '/define/etcd',
  component: Etcd,
  title: 'etcd Connection',
};

const submitDefinitionPage = {
  path: '/define/submit',
  component: SubmitDefinition,
  title: 'Submit',
  hidePager: true,
};

const successPage = {
  path: '/boot/complete',
  component: Success,
  title: 'Installation Complete',
  hidePager: true,
  hideSave: true,
};

const TFPowerOnPage = {
  path: '/boot/tf/poweron',
  component: TF_PowerOn,
  title: 'Start Installation',
};

const usersPage = {
  path: '/define/users',
  component: Users,
  title: 'Console Login',
};

// Baremetal pages
const bmClusterInfoPage = {
  path: '/define/cluster-info',
  component: BM_ClusterInfo,
  title: 'Cluster Info',
};

const bmControllersPage = {
  path: '/define/controllers',
  component: BM_Controllers,
  title: 'Define Masters',
};

const bmCredentialsPage = {
  path: '/define/credentials',
  component: BM_Credentials,
  title: 'Matchbox Credentials',
};

const bmHostnamePage = {
  path: '/define/hostname',
  component: BM_Hostname,
  title: 'Cluster DNS',
};

const bmMatchboxPage = {
  path: '/define/matchbox',
  component: BM_Matchbox,
  title: 'Matchbox Address',
};

const bmNetworkConfigPage = {
  path: '/define/network-config',
  component: BM_NetworkConfig,
  title: 'Network Configuration',
};

const bmSshKeysPage = {
  path: '/define/ssh-keys',
  component: BM_SSHKeys,
  title: 'SSH Key',
};

const bmWorkersPage = {
  path: '/define/workers',
  component: BM_Workers,
  title: 'Define Workers',
};

// AWS pages
const awsClusterInfoPage = {
  path: '/define/aws/cluster-info',
  component: AWS_ClusterInfo,
  title: 'Cluster Info',
};

const awsCloudCredentialsPage = {
  path: '/define/aws/cloud-credentials',
  component: AWS_CloudCredentials,
  title: 'AWS Credentials',
};

const awsDefineNodesPage = {
  path: '/define/aws/nodes',
  component: AWS_DefineNodes,
  title: 'Define Nodes',
};

const awsSubmitKeysPage = {
  path: '/define/aws/keys',
  component: AWS_SubmitKeys,
  title: 'SSH Key',
};

const awsVPCPage = {
  path: '/define/aws/vpc',
  component: AWS_VPC,
  title: 'Networking',
};

// This component is never visible!
const loadingPage = {
  path: '/',
  component: React.createElement('div'),
  title: 'Tectonic',
};

export const sections = new Map([
  ['loading', [
    loadingPage,
  ]],
  ['choose', [
    clusterTypePage,
  ]],
  ['defineBaremetal', [
    bmClusterInfoPage,
    bmHostnamePage,
    certificateAuthorityPage,
    bmMatchboxPage,
    bmCredentialsPage,
    bmNetworkConfigPage,
    bmControllersPage,
    bmWorkersPage,
    etcdPage,
    bmSshKeysPage,
    usersPage,
    submitDefinitionPage,
  ]],
  ['defineAWS', [
    awsCloudCredentialsPage,
    awsClusterInfoPage,
    certificateAuthorityPage,
    awsSubmitKeysPage,
    awsDefineNodesPage,
    awsVPCPage,
    usersPage,
    submitDefinitionPage,
  ]],
  ['bootBaremetalTF', [
    TFPowerOnPage,
    successPage,
  ]],
  ['bootAWSTF', [
    TFPowerOnPage,
    successPage,
  ]],
  ['bootDryRun', [
    dryRunPage,
  ]],
]);

// Lets us do 'sections.defineAWS' instead of using sections.get('defineAWS')
sections.forEach((v, k) => {
  sections[k] = v;
});

// A Trail is an immutable representation of the navigation options available to a user.
// Navigation involves
//    - moving from one page to the next page
//    - moving from one page to some previous pages
//    - presenting a (possibly disabled) list of all pages
export class Trail {
  constructor(trailSections, whitelist, opts={}) {
    this.canReset = opts.canReset;
    this.sections = trailSections;
    const sectionPages = this.sections.reduce((ls, l) => ls.concat(l), []);
    sectionPages.forEach(p => {
      if (!p.component) {
        throw new Error(`${p.title} has no component`);
      }
    });
    this._pages = !whitelist ? sectionPages : sectionPages.filter(p => whitelist.includes(p));
    this.ixByPage = this._pages.reduce((m, v, ix) => m.set(v, ix), ImmutableMap());
    this.pageByPath = this._pages.reduce((m, v) => m.set(v.path, v), ImmutableMap());
  }

  sectionFor(page) {
    return _.find(this.sections, s => s.includes(page));
  }

  navigable(page) {
    return this.ixByPage.has(page);
  }

  // Given a path, return a "better" path for this trail. Will not navigate to that path.
  fixPath(path, state) {
    const page = this.pageByPath.get(path);
    if (!page) {
      return this._pages[0].path;
    }

    // If the NEXT button on a previous page is disabled, you
    // shouldn't be able to see this page. Show the first page, before
    // or including this one, that won't allow forward navigation.
    const pred = this._pages.find(p => {
      return p === page || !(p.component.canNavigateForward ? p.component.canNavigateForward(state) : true);
    });

    return pred.path;
  }

  // Returns -1, 0, or 1, if a is before, the same as, or after b in the trail
  cmp(a, b) {
    return this.ixByPage.get(a) - this.ixByPage.get(b);
  }

  canNavigateForward(pageA, pageB, state) {
    const a = this.ixByPage.get(pageA);
    const b = this.ixByPage.get(pageB);

    if (!_.isFinite(a) || !_.isFinite(b)) {
      return false;
    }

    const start = Math.min(a, b);
    const end = Math.max(a, b);

    for (let i = start; i < end; i++) {
      const component = this._pages[i].component;
      if (!component.canNavigateForward) {
        continue;
      }
      if (!component.canNavigateForward(state)) {
        return false;
      }
    }
    return true;
  }

   // Returns the previous page in the trail if that page exists
  previousFrom(page) {
    const myIx = this.ixByPage.get(page);
    return this._pages[myIx - 1];
  }

  // Returns the next page in the trail if that exists
  nextFrom(page) {
    const myIx = this.ixByPage.get(page);
    return this._pages[myIx + 1];
  }
}

const doingStuff = ImmutableSet([commitPhases.REQUESTED, commitPhases.WAITING]);

const platformToSection = {
  [AWS_TF]: {
    choose: new Trail([sections.choose, sections.defineAWS]),
    define: new Trail([sections.defineAWS], [submitDefinitionPage]),
    boot: new Trail([sections.bootAWSTF]),
  },
  [BARE_METAL_TF]: {
    choose: new Trail([sections.choose, sections.defineBaremetal]),
    define: new Trail([sections.defineBaremetal], [submitDefinitionPage]),
    boot: new Trail([sections.bootBaremetalTF]),
  },
};

export const trail = ({cluster, clusterConfig, commitState}) => {
  let platform = clusterConfig[PLATFORM_TYPE];
  const { ready } = cluster;

  if (platform === '') {
    return new Trail([sections.loading]);
  }
  if (!isSupported(platform)) {
    return new Trail([sections.choose]);
  }

  const { phase } = commitState;
  const submitted = ready || (phase === commitPhases.SUCCEEDED);
  if (submitted) {
    if (clusterConfig[DRY_RUN]) {
      return new Trail([sections.bootDryRun], null, {canReset: true});
    }
    return platformToSection[platform].boot;
  }
  if (doingStuff.has(phase)) {
    return platformToSection[platform].define;
  }

  return platformToSection[platform].choose;
};

export const getAllRoutes = () => {
  // No components have the same path, so this is safe.
  // If a user guesses an invalid URL, they could get in a weird state. Oh well.
  let routes = [];
  _.each(sections, v => {
    _.each(v, o => {
      routes.push(o);
    });
  });
  return routes;
};
<commit_msg>frontend: Refactor getAllRoutes()<commit_after>import _ from 'lodash';
import React from 'react';
import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable';

import { commitPhases } from './actions';
import { PLATFORM_TYPE, DRY_RUN } from './cluster-config';

import { CertificateAuthority } from './components/certificate-authority';
import { ClusterType } from './components/cluster-type';
import { DryRun } from './components/dry-run';
import { Etcd } from './components/etcd';
import { SubmitDefinition } from './components/submit-definition';
import { Success } from './components/success';
import { TF_PowerOn } from './components/tf-poweron';
import { Users } from './components/users';

import { BM_ClusterInfo } from './components/bm-cluster-info';
import { BM_Controllers, BM_Workers } from './components/bm-nodeforms';
import { BM_Credentials } from './components/bm-credentials';
import { BM_Hostname } from './components/bm-hostname';
import { BM_Matchbox } from './components/bm-matchbox';
import { BM_NetworkConfig } from './components/bm-network-config';
import { BM_SSHKeys } from './components/bm-sshkeys';

import { AWS_CloudCredentials } from './components/aws-cloud-credentials';
import { AWS_ClusterInfo } from './components/aws-cluster-info';
import { AWS_DefineNodes } from './components/aws-define-nodes';
import { AWS_SubmitKeys } from './components/aws-submit-keys';
import { AWS_VPC } from './components/aws-vpc';

import {
  AWS_TF,
  BARE_METAL_TF,
  isSupported,
} from './platforms';

// Common pages
const certificateAuthorityPage = {
  path: '/define/certificate-authority',
  component: CertificateAuthority,
  title: 'Certificate Authority',
};

const clusterTypePage = {
  path: '/define/cluster-type',
  component: ClusterType,
  title: 'Platform',
  hideSave: true,
  showRestore: true,
};

const dryRunPage = {
  path: '/define/advanced',
  component: DryRun,
  title: 'Download Assets',
};

const etcdPage = {
  path: '/define/etcd',
  component: Etcd,
  title: 'etcd Connection',
};

const submitDefinitionPage = {
  path: '/define/submit',
  component: SubmitDefinition,
  title: 'Submit',
  hidePager: true,
};

const successPage = {
  path: '/boot/complete',
  component: Success,
  title: 'Installation Complete',
  hidePager: true,
  hideSave: true,
};

const TFPowerOnPage = {
  path: '/boot/tf/poweron',
  component: TF_PowerOn,
  title: 'Start Installation',
};

const usersPage = {
  path: '/define/users',
  component: Users,
  title: 'Console Login',
};

// Baremetal pages
const bmClusterInfoPage = {
  path: '/define/cluster-info',
  component: BM_ClusterInfo,
  title: 'Cluster Info',
};

const bmControllersPage = {
  path: '/define/controllers',
  component: BM_Controllers,
  title: 'Define Masters',
};

const bmCredentialsPage = {
  path: '/define/credentials',
  component: BM_Credentials,
  title: 'Matchbox Credentials',
};

const bmHostnamePage = {
  path: '/define/hostname',
  component: BM_Hostname,
  title: 'Cluster DNS',
};

const bmMatchboxPage = {
  path: '/define/matchbox',
  component: BM_Matchbox,
  title: 'Matchbox Address',
};

const bmNetworkConfigPage = {
  path: '/define/network-config',
  component: BM_NetworkConfig,
  title: 'Network Configuration',
};

const bmSshKeysPage = {
  path: '/define/ssh-keys',
  component: BM_SSHKeys,
  title: 'SSH Key',
};

const bmWorkersPage = {
  path: '/define/workers',
  component: BM_Workers,
  title: 'Define Workers',
};

// AWS pages
const awsClusterInfoPage = {
  path: '/define/aws/cluster-info',
  component: AWS_ClusterInfo,
  title: 'Cluster Info',
};

const awsCloudCredentialsPage = {
  path: '/define/aws/cloud-credentials',
  component: AWS_CloudCredentials,
  title: 'AWS Credentials',
};

const awsDefineNodesPage = {
  path: '/define/aws/nodes',
  component: AWS_DefineNodes,
  title: 'Define Nodes',
};

const awsSubmitKeysPage = {
  path: '/define/aws/keys',
  component: AWS_SubmitKeys,
  title: 'SSH Key',
};

const awsVPCPage = {
  path: '/define/aws/vpc',
  component: AWS_VPC,
  title: 'Networking',
};

// This component is never visible!
const loadingPage = {
  path: '/',
  component: React.createElement('div'),
  title: 'Tectonic',
};

export const sections = new Map([
  ['loading', [
    loadingPage,
  ]],
  ['choose', [
    clusterTypePage,
  ]],
  ['defineBaremetal', [
    bmClusterInfoPage,
    bmHostnamePage,
    certificateAuthorityPage,
    bmMatchboxPage,
    bmCredentialsPage,
    bmNetworkConfigPage,
    bmControllersPage,
    bmWorkersPage,
    etcdPage,
    bmSshKeysPage,
    usersPage,
    submitDefinitionPage,
  ]],
  ['defineAWS', [
    awsCloudCredentialsPage,
    awsClusterInfoPage,
    certificateAuthorityPage,
    awsSubmitKeysPage,
    awsDefineNodesPage,
    awsVPCPage,
    usersPage,
    submitDefinitionPage,
  ]],
  ['bootBaremetalTF', [
    TFPowerOnPage,
    successPage,
  ]],
  ['bootAWSTF', [
    TFPowerOnPage,
    successPage,
  ]],
  ['bootDryRun', [
    dryRunPage,
  ]],
]);

// Lets us do 'sections.defineAWS' instead of using sections.get('defineAWS')
sections.forEach((v, k) => {
  sections[k] = v;
});

// A Trail is an immutable representation of the navigation options available to a user.
// Navigation involves
//    - moving from one page to the next page
//    - moving from one page to some previous pages
//    - presenting a (possibly disabled) list of all pages
export class Trail {
  constructor(trailSections, whitelist, opts={}) {
    this.canReset = opts.canReset;
    this.sections = trailSections;
    const sectionPages = this.sections.reduce((ls, l) => ls.concat(l), []);
    sectionPages.forEach(p => {
      if (!p.component) {
        throw new Error(`${p.title} has no component`);
      }
    });
    this._pages = !whitelist ? sectionPages : sectionPages.filter(p => whitelist.includes(p));
    this.ixByPage = this._pages.reduce((m, v, ix) => m.set(v, ix), ImmutableMap());
    this.pageByPath = this._pages.reduce((m, v) => m.set(v.path, v), ImmutableMap());
  }

  sectionFor(page) {
    return _.find(this.sections, s => s.includes(page));
  }

  navigable(page) {
    return this.ixByPage.has(page);
  }

  // Given a path, return a "better" path for this trail. Will not navigate to that path.
  fixPath(path, state) {
    const page = this.pageByPath.get(path);
    if (!page) {
      return this._pages[0].path;
    }

    // If the NEXT button on a previous page is disabled, you
    // shouldn't be able to see this page. Show the first page, before
    // or including this one, that won't allow forward navigation.
    const pred = this._pages.find(p => {
      return p === page || !(p.component.canNavigateForward ? p.component.canNavigateForward(state) : true);
    });

    return pred.path;
  }

  // Returns -1, 0, or 1, if a is before, the same as, or after b in the trail
  cmp(a, b) {
    return this.ixByPage.get(a) - this.ixByPage.get(b);
  }

  canNavigateForward(pageA, pageB, state) {
    const a = this.ixByPage.get(pageA);
    const b = this.ixByPage.get(pageB);

    if (!_.isFinite(a) || !_.isFinite(b)) {
      return false;
    }

    const start = Math.min(a, b);
    const end = Math.max(a, b);

    for (let i = start; i < end; i++) {
      const component = this._pages[i].component;
      if (!component.canNavigateForward) {
        continue;
      }
      if (!component.canNavigateForward(state)) {
        return false;
      }
    }
    return true;
  }

   // Returns the previous page in the trail if that page exists
  previousFrom(page) {
    const myIx = this.ixByPage.get(page);
    return this._pages[myIx - 1];
  }

  // Returns the next page in the trail if that exists
  nextFrom(page) {
    const myIx = this.ixByPage.get(page);
    return this._pages[myIx + 1];
  }
}

const doingStuff = ImmutableSet([commitPhases.REQUESTED, commitPhases.WAITING]);

const platformToSection = {
  [AWS_TF]: {
    choose: new Trail([sections.choose, sections.defineAWS]),
    define: new Trail([sections.defineAWS], [submitDefinitionPage]),
    boot: new Trail([sections.bootAWSTF]),
  },
  [BARE_METAL_TF]: {
    choose: new Trail([sections.choose, sections.defineBaremetal]),
    define: new Trail([sections.defineBaremetal], [submitDefinitionPage]),
    boot: new Trail([sections.bootBaremetalTF]),
  },
};

export const trail = ({cluster, clusterConfig, commitState}) => {
  let platform = clusterConfig[PLATFORM_TYPE];
  const { ready } = cluster;

  if (platform === '') {
    return new Trail([sections.loading]);
  }
  if (!isSupported(platform)) {
    return new Trail([sections.choose]);
  }

  const { phase } = commitState;
  const submitted = ready || (phase === commitPhases.SUCCEEDED);
  if (submitted) {
    if (clusterConfig[DRY_RUN]) {
      return new Trail([sections.bootDryRun], null, {canReset: true});
    }
    return platformToSection[platform].boot;
  }
  if (doingStuff.has(phase)) {
    return platformToSection[platform].define;
  }

  return platformToSection[platform].choose;
};

// No components have the same path, so this is safe.
// If a user guesses an invalid URL, they could get in a weird state. Oh well.
export const getAllRoutes = () => _.uniq(_.flatMap(sections));
<|endoftext|>


<commit_before>(function ($, window, document) {
    var StickUp = function(elem, opts) {
        var contentTop = [],
        content = [],
        lastScrollTop = 0,
        scrollDir = '',
        itemHover = '',
        menuSize = null,
        active = false,
        stickyHeight = 0,
        outerHeight = 0,
        currentMarginT = 0,
        $element = $(),
        topMargin = 0,
        offset = 0,
        scrollDistance = 0,
        $placeholder = $('

'),
        top,
        //defaults
        options = {
            scrollHide: true,
            topMargin: "auto"
        },
        getTopMargin = function () {
            if (options.topMargin === 'auto') {
                return parseInt($element.css('marginTop'));
            } else {
                if (isNaN(options.topMargin) && options.topMargin.search("px") > 0) {
                    return parseInt(options.topMargin.replace("px", ""));
                } else if (!isNaN(parseInt(options.topMargin))) {
                    return parseInt(options.topMargin);
                } else {
                    console.log("incorrect argument, ignored.");
                    return 0;
                }
            }
        },
        stickUpScrollHandlerFn = function (event) {
            var scroll = $(event.target).scrollTop();
            scrollDir = (scroll >= lastScrollTop) ? 'down' : 'up';
            scrollDistance = Math.abs(lastScrollTop - scroll);
            lastScrollTop = scroll;

            // permanently measure initial sizes to avoid errors with loading images
            // and dynamic elements in document
            if (!active) {
                stickyHeight = parseInt($element.outerHeight()+topMargin);
                outerHeight = parseInt($element.outerHeight(true));
                top = parseInt($element.offset().top);
            }

            // Google like reappearance on upward scroll
            if (options.scrollHide) {
                offset = stickyHeight;
                if (active) {
                    var topValue = parseInt($element.css('top'));
                    var maxTop = stickyHeight;
                    if (scrollDir === 'up') {
                        var newTopValue = scrollDistance > -topValue ? 0 : topValue + scrollDistance;
                        $element.css('top', newTopValue + 'px');
                    } else if (scrollDir === "down" && topValue > -maxTop) {
                        var newTopValue = scrollDistance > maxTop + topValue ? -maxTop : topValue - scrollDistance;
                        $element.css('top', newTopValue + 'px');
                    }
                }
            }

            //STICK IT
            if (top + offset < scroll + topMargin && !$element.hasClass('isStuck')) {
                window.requestAnimationFrame(function () {
                    active = true;
                    $element.before($placeholder.css('height', outerHeight));
                    $element.addClass('isStuck');

                    var topDistance = -offset;

                    $element.css({
                        marginTop: topMargin,
                        position: "fixed",
                        top: topDistance + 'px'
                    });
                });
            }

            //UNSTICK
            if (scroll + topMargin <= top && $element.hasClass('isStuck')) {
                window.requestAnimationFrame(function () {
                    $placeholder.remove();
                    $element.removeClass('isStuck')
                    .css({
                        marginTop: "",
                        position: "relative",
                        top: ""
                    });

                    active = false;
                });
            }
        };
        //init
        var initialize = function(elem,opts){
            $element = $(elem);
            // adding a class to users div
            $element.addClass('stuckElement');
            //getting options
            if (opts) {
                $.extend(true, options, opts);
            }
            topMargin = (options.topMargin != null) ? getTopMargin() : 0;

            $(document).on('scroll.stickUp', stickUpScrollHandlerFn);
            //initial round ;-)
            stickUpScrollHandlerFn({target: document});
        };
        initialize.call(this, elem, opts);
    };

    $.fn.stickUp = function( options ) {
        return this.each(function() {
          new StickUp( this, options );
        });
    };
}(jQuery, window, document));<commit_msg>fixing topMargin issue<commit_after>(function ($, window, document) {
    var StickUp = function(elem, opts) {
        var contentTop = [],
        content = [],
        lastScrollTop = 0,
        scrollDir = '',
        itemHover = '',
        menuSize = null,
        active = false,
        stickyHeight = 0,
        outerHeight = 0,
        currentMarginT = 0,
        $element = $(),
        topMargin = 0,
        offset = 0,
        scrollDistance = 0,
        $placeholder = $('

'),
        top,
        left,
        //defaults
        options = {
            scrollHide: true,
            topMargin: "auto"
        },
        getTopMargin = function () {
            if (options.topMargin === 'auto') {
                return parseInt($element.css('marginTop'));
            } else {
                if (isNaN(options.topMargin) && options.topMargin.search("px") > 0) {
                    return parseInt(options.topMargin.replace("px", ""));
                } else if (!isNaN(parseInt(options.topMargin))) {
                    return parseInt(options.topMargin);
                } else {
                    console.log("incorrect argument, ignored.");
                    return 0;
                }
            }
        },
        stickUpScrollHandlerFn = function (event) {
            var scroll = $(event.target).scrollTop();
            scrollDir = (scroll >= lastScrollTop) ? 'down' : 'up';
            scrollDistance = Math.abs(lastScrollTop - scroll);
            lastScrollTop = scroll;

            // permanently measure initial sizes to avoid errors with loading images
            // and dynamic elements in document
            if (!active) {
                stickyHeight = parseInt($element.outerHeight()+topMargin);
                outerHeight = parseInt($element.outerHeight(true));
                top = parseInt($element.offset().top);
                left = parseInt($element.offset().left);
            }

            // Google like reappearance on upward scroll
            if (options.scrollHide) {
                offset = stickyHeight;
                if (active) {
                    var topValue = parseInt($element.css('top'));
                    var maxTop = stickyHeight;
                    if (scrollDir === 'up') {
                        var newTopValue = scrollDistance > -topValue ? 0 : topValue + scrollDistance;
                        $element.css('top', newTopValue + 'px');
                    } else if (scrollDir === "down" && topValue > -maxTop) {
                        var newTopValue = scrollDistance > maxTop + topValue ? -maxTop : topValue - scrollDistance;
                        $element.css('top', newTopValue + 'px');
                    }
                }
            }

            //STICK IT
            if (top + offset < scroll + topMargin && !$element.hasClass('isStuck')) {
                window.requestAnimationFrame(function () {
                    active = true;
                    $element.before($placeholder.css('height', outerHeight));
                    $element.addClass('isStuck');

                    var topDistance = -offset;

                    $element.css({
                        //maxWidth: parseInt($element.outerWidth()),
                        marginTop: topMargin,
                        position: "fixed",
                        top: topDistance + 'px',
                        left: left,
                        right: "auto",
                    });
                });
            }

            //UNSTICK
            if (scroll + topMargin <= top && $element.hasClass('isStuck')) {
                window.requestAnimationFrame(function () {
                    $placeholder.remove();
                    $element.removeClass('isStuck')
                    .css({
                        maxWidth:"",
                        marginTop: "",
                        position: "",
                        top: "",
                        left: "",
                        right: ""
                    });

                    active = false;
                });
            }
        };
        //init
        var initialize = function(elem,opts){
            $element = $(elem);
            // adding a class to users div
            $element.addClass('stuckElement');
            //getting options
            if (opts) {
                $.extend(true, options, opts);
            }
            topMargin = (options.topMargin != null) ? getTopMargin() : 0;

            $(document).on('scroll.stickUp', stickUpScrollHandlerFn);
            //initial round ;-)
            stickUpScrollHandlerFn({target: document});
        };
        initialize.call(this, elem, opts);
    };

    $.fn.stickUp = function( options ) {
        return this.each(function() {
          new StickUp( this, options );
        });
    };
}(jQuery, window, document));<|endoftext|>


<commit_before>define([
    'plugins/router',
    'knockout',
    'ko-mapping',
    'lib/models/Class',
    'lib/environment',
    'lib/vstack',
    'ko-postbox'
],
function (router, ko, mapping, Class, env, vstack, postbox) {

    var fs = require('fs');
    var q = require('q');
    var colors = require('colors/safe');

    var Addon = Class.extend({
        init: function(data, dataMap) {
            this.identifier = 'Addon';
            if (typeof data == 'string') {
                this.initFromConfig(data, dataMap || {});
            } else {
                this.initFromArray(data, dataMap || {});
            }
        },
        initFromArray: function(data, dataMap) {

            this.dragging = ko.observable(false);
            this.project = ko.observable().syncWith('project.main', true);
            this.sideNavTypes = ko.observableArray().subscribeTo('project.sideNavTypes', true);
            var settings = this.project().settings()[this.name];

            if (settings !== undefined) {
                data.data = settings;
            }

            var addonMap = {
                'copy': ["id", "name", "title", "type", "version"],
                'data': {
                    create: function (options) {
                        if (typeof options.data == 'string') {
                            options.data = JSON.parse(options.data);
                        }
                        return ko.observable(new function () {
                            mapping.fromJS(options.data, dataMap, this);
                        });
                    }
                },
                'modules': {
                    create: function (options) {
                        if (typeof options.data == 'string') {
                            return JSON.parse(options.data);
                        } else {
                            return options.data;
                        }
                    }
                }
            };

            data.enabled = data.enabled || 0;
            data.data = data.data || {};
            data.modules = data.modules || {};

            mapping.fromJS(data, addonMap, this);

            //@todo: clean this up as this is a hacky bit of code.
            ko.computed(function() {
                this.sideNavTypes().forEach(function (menuItem, key) {
                    if (menuItem.name == this.type) {

                        var moduleKey = 0, moduleRoute;
                        menuItem.menu().forEach(function (item, index) {
                            if (item.route == this.name) {
                                moduleKey = index;
                                moduleRoute = item.route;
                            }
                        }.bind(this));

                        var project = this.project.peek();

                        if (menuItem.menu()[moduleKey - 1] !== undefined) {
                            this.prev = function () {
                                router.navigate(project.editUrl() + '/' + menuItem.menu()[moduleKey - 1].route);
                            };
                        } else if (this.sideNavTypes()[key - 1] != undefined) {
                            this.prev = function () {
                                router.navigate(project.editUrl() + '/' + this.sideNavTypes()[key - 1].menu().pop().route);
                            };
                        } else {
                            this.prev = undefined
                        }
                        if (menuItem.menu()[moduleKey + 1] !== undefined) {
                            this.next = function () {
                                router.navigate(project.editUrl() + '/' + menuItem.menu()[moduleKey + 1].route);
                            };
                        } else if (this.sideNavTypes()[key + 1] != undefined) {
                            this.next = function () {
                                router.navigate(project.editUrl() + '/' + this.sideNavTypes()[key + 1].menu()[0].route);
                            };
                        } else {
                            this.next = undefined
                        }
                    }
                }.bind(this));
            }, this);

        },
        initFromConfig: function(name, dataMap) {
            var basePath = process.cwd();
            var config = JSON.parse(fs.readFileSync([basePath, 'app', 'addons', name, 'config.json'].join(env.pathSeparator()), 'utf8'));
            this.initFromArray(config, dataMap);
        },
        save: function() {
            var deferred = q.defer();
            vstack.db.transaction(function (tx) {
                var sql = "UPDATE addon SET name=?, enabled=?, title=?, type=?, version=?, priority=? WHERE name=?";
                tx.executeSql(sql, [
                    this.name,
                    this.enabled() ? 1 : 0,
                    this.title,
                    this.type,
                    this.version,
                    this.priority(),
                    this.name
                ], function (tx, results) {
                    deferred.resolve(true);
                }, function(tx, error) {
                    console.error(error);
                    deferred.resolve(false);
                });
            }.bind(this));
            return deferred.promise;
        },
        enableLiveUpdates: function() {
            /**
             * Save back to main project on change.
             */
            ko.computed(function () {
                /*
                 * Need to convert using ko.toJSON first to allow model override using this.toJSON.
                 * ko.toJSON is also needed to trigger this computed variable as mapping does not do that.
                 * mapping.toJS removes the mapping data.
                 */
                var data = JSON.parse(ko.toJSON(this.data()));
                this.project.peek().settings.peek()[this.name] = mapping.toJS(data, {}); // Block change on project.
                console.log(this.project.peek().settings.peek());
            }, this);
        }
    });

    var find = function(condition) {
        var deferred = q.defer();
        var sql = 'SELECT a.rowid as id,a.*,t.priority AS t_priority FROM addon a LEFT JOIN type t ON a.type = t.name';
        condition = condition || [];
        if (condition.length > 0) {
            sql += ' WHERE ';
        }
        condition.forEach(function() {
            sql += condition;
        });
        sql += ' ORDER BY t.priority, a.priority';
        vstack.db.transaction(function (tx) {
            tx.executeSql(sql, [], function (tx, results) {
                var len = results.rows.length, i, data = [];
                for (i = 0; i < len; i++) {
                    var row = results.rows.item(i);
                    var addon = new Addon(row);
                    data.push(addon);
                }
                deferred.resolve(data);
            });
        });
        return deferred.promise;
    };

    Addon.findAllTypes = function() {
        var deferred = q.defer();
        var sql = 'SELECT a.type, t.priority FROM addon a LEFT JOIN type t ON a.type = t.name GROUP BY a.type ORDER BY t.priority, a.priority';
        vstack.db.transaction(function (tx) {
            tx.executeSql(sql, [], function (tx, results) {
                deferred.resolve(results);
            });
        });
        return deferred.promise;
    };

    Addon.findByType = function(type) {
        var deferred = q.defer();
        find(['type="'+type+'"']).then(function(data) {
            deferred.resolve(data);
        });
        return deferred.promise;
    };

    Addon.findOne = function() {
        var deferred = q.defer();
        find().then(function(data) {
            if (data) {
                deferred.resolve(data[0]);
            } else {
                deferred.resolve(data);
            }
        });
        return deferred.promise;
    };

    Addon.findAll = function() {
        var deferred = q.defer();
        find().then(function(data) {
            deferred.resolve(data);
        });
        return deferred.promise;
    };

    Addon.findEnabled = function() {
        var deferred = q.defer();
        find(['enabled="1"']).then(function(data) {
            deferred.resolve(data);
        });
        return deferred.promise;
    };

    return Addon;
});<commit_msg>fix for mapping.<commit_after>define([
    'plugins/router',
    'knockout',
    'ko-mapping',
    'lib/models/Class',
    'lib/environment',
    'lib/vstack',
    'ko-postbox'
],
function (router, ko, mapping, Class, env, vstack, postbox) {

    var fs = require('fs');
    var q = require('q');
    var colors = require('colors/safe');

    var Addon = Class.extend({
        init: function(data, dataMap) {
            this.identifier = 'Addon';
            if (typeof data == 'string') {
                this.initFromConfig(data, dataMap || {});
            } else {
                this.initFromArray(data, dataMap || {});
            }
        },
        initFromArray: function(data, dataMap) {

            this.dragging = ko.observable(false);
            this.project = ko.observable().syncWith('project.main', true);
            this.sideNavTypes = ko.observableArray().subscribeTo('project.sideNavTypes', true);
            var settings = this.project().settings()[this.name];

            if (settings !== undefined) {
                data.data = settings;
            }

            var addonMap = {
                'copy': ["id", "name", "title", "type", "version"],
                'data': {
                    create: function (options) {
                        if (typeof options.data == 'string') {
                            options.data = JSON.parse(options.data);
                        }
                        return ko.observable(new function () {
                            mapping.fromJS(options.data, dataMap, this);
                        });
                    }
                },
                'modules': {
                    create: function (options) {
                        if (typeof options.data == 'string') {
                            return JSON.parse(options.data);
                        } else {
                            return options.data;
                        }
                    }
                }
            };

            data.enabled = data.enabled || 0;
            data.data = data.data || {};
            data.modules = data.modules || {};

            mapping.fromJS(data, addonMap, this);

            //@todo: clean this up as this is a hacky bit of code.
            ko.computed(function() {
                this.sideNavTypes().forEach(function (menuItem, key) {
                    if (menuItem.name == this.type) {

                        var moduleKey = 0, moduleRoute;
                        menuItem.menu().forEach(function (item, index) {
                            if (item.route == this.name) {
                                moduleKey = index;
                                moduleRoute = item.route;
                            }
                        }.bind(this));

                        var project = this.project.peek();

                        if (menuItem.menu()[moduleKey - 1] !== undefined) {
                            this.prev = function () {
                                router.navigate(project.editUrl() + '/' + menuItem.menu()[moduleKey - 1].route);
                            };
                        } else if (this.sideNavTypes()[key - 1] != undefined) {
                            this.prev = function () {
                                router.navigate(project.editUrl() + '/' + this.sideNavTypes()[key - 1].menu().pop().route);
                            };
                        } else {
                            this.prev = undefined
                        }
                        if (menuItem.menu()[moduleKey + 1] !== undefined) {
                            this.next = function () {
                                router.navigate(project.editUrl() + '/' + menuItem.menu()[moduleKey + 1].route);
                            };
                        } else if (this.sideNavTypes()[key + 1] != undefined) {
                            this.next = function () {
                                router.navigate(project.editUrl() + '/' + this.sideNavTypes()[key + 1].menu()[0].route);
                            };
                        } else {
                            this.next = undefined
                        }
                    }
                }.bind(this));
            }, this);

        },
        initFromConfig: function(name, dataMap) {
            var basePath = process.cwd();
            var config = JSON.parse(fs.readFileSync([basePath, 'app', 'addons', name, 'config.json'].join(env.pathSeparator()), 'utf8'));
            this.initFromArray(config, dataMap);
        },
        save: function() {
            var deferred = q.defer();
            vstack.db.transaction(function (tx) {
                var sql = "UPDATE addon SET name=?, enabled=?, title=?, type=?, version=?, priority=? WHERE name=?";
                tx.executeSql(sql, [
                    this.name,
                    this.enabled() ? 1 : 0,
                    this.title,
                    this.type,
                    this.version,
                    this.priority(),
                    this.name
                ], function (tx, results) {
                    deferred.resolve(true);
                }, function(tx, error) {
                    console.error(error);
                    deferred.resolve(false);
                });
            }.bind(this));
            return deferred.promise;
        },
        enableLiveUpdates: function() {
            /**
             * Save back to main project on change.
             */
            ko.computed(function () {
                /*
                 * Need to convert using ko.toJSON first to allow model override using this.toJSON.
                 * ko.toJSON is also needed to trigger this computed variable as mapping does not do that.
                 * mapping.toJS removes the mapping data.
                 */
                var data = JSON.parse(ko.toJSON(this.data()));
                this.project.peek().settings.peek()[this.name] = mapping.toJS(data); // Block change on project.
                console.log(this.project.peek().settings.peek());
            }, this);
        }
    });

    var find = function(condition) {
        var deferred = q.defer();
        var sql = 'SELECT a.rowid as id,a.*,t.priority AS t_priority FROM addon a LEFT JOIN type t ON a.type = t.name';
        condition = condition || [];
        if (condition.length > 0) {
            sql += ' WHERE ';
        }
        condition.forEach(function() {
            sql += condition;
        });
        sql += ' ORDER BY t.priority, a.priority';
        vstack.db.transaction(function (tx) {
            tx.executeSql(sql, [], function (tx, results) {
                var len = results.rows.length, i, data = [];
                for (i = 0; i < len; i++) {
                    var row = results.rows.item(i);
                    var addon = new Addon(row);
                    data.push(addon);
                }
                deferred.resolve(data);
            });
        });
        return deferred.promise;
    };

    Addon.findAllTypes = function() {
        var deferred = q.defer();
        var sql = 'SELECT a.type, t.priority FROM addon a LEFT JOIN type t ON a.type = t.name GROUP BY a.type ORDER BY t.priority, a.priority';
        vstack.db.transaction(function (tx) {
            tx.executeSql(sql, [], function (tx, results) {
                deferred.resolve(results);
            });
        });
        return deferred.promise;
    };

    Addon.findByType = function(type) {
        var deferred = q.defer();
        find(['type="'+type+'"']).then(function(data) {
            deferred.resolve(data);
        });
        return deferred.promise;
    };

    Addon.findOne = function() {
        var deferred = q.defer();
        find().then(function(data) {
            if (data) {
                deferred.resolve(data[0]);
            } else {
                deferred.resolve(data);
            }
        });
        return deferred.promise;
    };

    Addon.findAll = function() {
        var deferred = q.defer();
        find().then(function(data) {
            deferred.resolve(data);
        });
        return deferred.promise;
    };

    Addon.findEnabled = function() {
        var deferred = q.defer();
        find(['enabled="1"']).then(function(data) {
            deferred.resolve(data);
        });
        return deferred.promise;
    };

    return Addon;
});<|endoftext|>


<commit_before>/**
 * The StoriesEdit view.
 */
define([
  'jquery',
  'backbone',
  'mps'
], function($,Backbone,mps) {

  'use strict';

  var config = {
    ZOOM: 3,
    MINZOOM: 3,
    MAXZOOM: 20,
    LAT: 15,
    LNG: 27,
    ISO: 'ALL',
    BASEMAP: 'grayscale',
    BASELAYER: 'loss',
    MONTHNAMES: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
    MONTHNAMES_SHORT: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
    QUARTERNAMES: ["JAN - MAR", "APR - JUN", "JUL - SEP", "OCT - DEC"],
    mapLoaded: false
  };

  config.MAPOPTIONS = {
    zoom: config.ZOOM,
    minZoom: config.MINZOOM,
    maxZoom: config.MAXZOOM,
    center: new google.maps.LatLng(config.LAT, config.LNG),
    mapTypeId: google.maps.MapTypeId.HYBRID,
    backgroundColor: '#99b3cc',
    disableDefaultUI: true,
  };

  config.OVERLAYSTYLES = {
    strokeWeight: 2,
    fillOpacity: 0.45,
    fillColor: "#FFF",
    strokeColor: "#A2BC28",
    editable: true,
    icon: new google.maps.MarkerImage(
      '/assets/icons/marker_exclamation.png',
      new google.maps.Size(36, 36), // size
      new google.maps.Point(0, 0), // offset
      new google.maps.Point(18, 18) // anchor
    )
  };

  config.MAPSTYLES = {};

  config.MAPSTYLES.grayscale = {
    type: 'style',
    style: [ { "featureType": "water" }, { "featureType": "transit", "stylers": [ { "saturation": -100 } ] }, { "featureType": "road", "stylers": [ { "saturation": -100 } ] }, { "featureType": "poi", "stylers": [ { "saturation": -100 } ] }, { "featureType": "landscape", "stylers": [ { "saturation": -100 } ] }, { "featureType": "administrative", "stylers": [ { "saturation": -100 } ] },{ "featureType": "poi.park", "elementType": "geometry", "stylers": [ { "visibility": 'off' } ] } ]
  }

  config.MAPSTYLES.terrain = {
    type: 'mapType',
    style: google.maps.MapTypeId.TERRAIN,
    title: "Terrain"
  }

  config.MAPSTYLES.satellite = {
    type: 'mapType',
    style: google.maps.MapTypeId.SATELLITE,
    title: "Satellite"
  }

  config.MAPSTYLES.roads = {
    type: 'mapType',
    style: google.maps.MapTypeId.HYBRID,
    title: "Roads"
  }

  config.MAPSTYLES.treeheight = {
    type: 'customMapType',
    style: new google.maps.ImageMapType({
      getTileUrl: function(ll, z) {
        var X = Math.abs(ll.x % (1 << z)); // wrap
        return window.gfw.config.GFW_API_HOST + "/gee/simple_green_coverage/" + z + "/" + X + "/" + ll.y + ".png";
      },
      tileSize: new google.maps.Size(256, 256),
      isPng: true,
      maxZoom: 17,
      name: "Forest Height",
      alt: "Global forest height"
    })
  }

  config.MAPSTYLES.landsat = [];

  for(var i = 1999; i < 2013; i++) {
    (function(year) {
      config.MAPSTYLES.landsat[i] = new google.maps.ImageMapType({
        getTileUrl: function(ll, z) {
          var X = Math.abs(ll.x % (1 << z));  // wrap
          return window.gfw.config.GFW_API_HOST + "/gee/landsat_composites/" + z + "/" + X + "/" + ll.y + ".png?year="+year;
        },
        tileSize: new google.maps.Size(256, 256),
        isPng: true,
        maxZoom: 17,
        name: "Landsat "+i
      });
    })(i);
  }

  var StoriesEditModel = Backbone.Model.extend({
    defaults: {
      the_geom: null
    }
  })

  var StoriesEditView = Backbone.View.extend({

    el: '#storiesEditView',

    events: {
      'click #zoomIn': '_zoomIn',
      'click #zoomOut': '_zoomOut',
      'click #autoLocate': '_autoLocate'
    },

    initialize: function() {
      if (!this.$el.length) {
        return
      }

      this.model = new StoriesEditModel();

      this.uploadsIds = [];
      this.filesAdded = 0;

      this._initViews();
      this._initBindings();

      this.render();
    },

    _initBindings: function() {
      var that = this;

      $('.upload_picture').on('click', function(e) {
        e.preventDefault();

        $('#fileupload').click();
      });

      $('#fileupload').fileupload({
          url: '/media/upload',
          dataType: 'json',
          autoUpload: true,
          acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
          maxFileSize: 4000000, // 4 MB
          // Enable image resizing, except for Android and Opera,
          // which actually support image resizing, but fail to
          // send Blob objects via XHR requests:
          disableImageResize: /Android(?!.*Chrome)|Opera/
            .test(window.navigator.userAgent),
          previewMaxWidth: 132,
          previewMaxHeight: 76,
          previewCrop: true,
          timeout: 3600000
      }).on('fileuploadadd', function (e, data) {
        data.context = $('
').appendTo('#files');

        that.filesAdded += _.size(data.files);
        _.each(data.files, function(file) {
          if (file && file.size > 4000000) {
            mps.publish('Notification/open', ['notif-limit-exceed']);
            data.abort();
          } else {
            var filename = that.prettifyFilename(file.name);
            var $thumbnail = $("

"+ file.name +"

<svg><use xlink:href='#shape-spinner'></use></svg>

");
            $('.thumbnails').append($thumbnail);
            $thumbnail.fadeIn(250);

            var $submitButton = $("form input[type='submit']");
            $submitButton.addClass('disabled');
            $submitButton.attr('disabled', 'disabled');
            $submitButton.val('Please wait...');
          }
        });

        // data.submit();
      }).on('fileuploaddone', function (e, data) {
        var files = [data.result];

        $.each(files, function (index, file) {
          that.filesAdded--;
          that.uploadsIds.push(file.basename);

          var url = file.url.replace('https', 'http');
          var $thumb = $("

<svg><use xlink:href='#shape-close'></use></svg>
");

          var filename = that.prettifyFilename(file.basename).substring(45);

          $(".thumbnail[data-name='"+filename+"']").fadeOut(250, function() {
            $(this).remove();

            $(".thumbnails").append($thumb);
            $thumb.fadeIn(250);
          });

          $thumb.find('.destroy').on('click', function(e) {
            e.preventDefault();

            var confirmation = confirm('Are you sure?')

            if (confirmation == true) {
              that.uploadsIds = _.without(that.uploadsIds, file.basename);
              $("#story_uploads_ids").val(that.uploadsIds.join(","));

              $thumb.fadeOut(250, function() {
                $thumb.remove();
              });
            }
          });
        });

        if (that.filesAdded <= 0) {
          var $submitButton = $("form input[type='submit']");
          $submitButton.val('Submit story');
          $submitButton.removeClass('disabled');
          $submitButton.attr('disabled', false);
        }

        $('#story_uploads_ids').val(that.uploadsIds.join(','));
        ga('send', 'event', 'Stories', 'New story', 'submit');
      }).on('fileuploadfail', function (e, data){
        mps.publish('Notification/open', ['upload-error-server']);
        var $submitButton = $("form input[type='submit']");
        $submitButton.val('Submit story');
        $submitButton.removeClass('disabled');
        $submitButton.attr('disabled', false);
      });
    },

    _initViews: function() {
      var that = this;
      var $searchInput = $('.map-search-input');

      // Load map
      this.map = new google.maps.Map(document.getElementById('stories_map'),config.MAPOPTIONS);

      // Listen to map loaded
      google.maps.event.addListenerOnce(this.map, 'idle', _.bind(function(){
        this._autoLocate();
      }, this ));

      // Set autocomplete search input
      this.autocomplete = new google.maps.places.Autocomplete($searchInput[0], {types: ['geocode']});

      // Listen to selected areas (search)
      google.maps.event.addListener(this.autocomplete, 'place_changed', _.bind(function() {
        var place = this.autocomplete.getPlace();
        if (place && place.geometry && place.geometry.viewport) {
          this.map.fitBounds(place.geometry.viewport)
        }
        if (place && place.geometry && place.geometry.location && !place.geometry.viewport) {
          var index = [];
          for (var x in place.geometry.location) {
             index.push(x);
          }
          this.map.setCenter(new google.maps.LatLng(place.geometry.location[index[0]], place.geometry.location[index[1]]));
        }
      }, this ));

      google.maps.event.addDomListener($searchInput[0], 'keydown', function(e) {
        if (e.keyCode == 13) {
          e.preventDefault();
        }
      });

      // Listen to any change on center position
      google.maps.event.addListener(this.map, 'zoom_changed',
        _.bind(function() {
          this.setCenter();
        }, this)
      );
      google.maps.event.addListener(this.map, 'dragend',
        _.bind(function() {
          this.setCenter();
      }, this));

    },

    _autoLocate: function(e){
      this.$autoLocate.addClass('active');
      if(navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
          _.bind(function(position) {
            this.$autoLocate.removeClass('active');
            var pos = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
            this.map.panTo(pos);
            this.setCenter();
            this.setZoom(18);
          }, this ),
          _.bind(function() {
            this.$autoLocate.removeClass('active');
            mps.publish('Notification/open', ['notif-enable-location']);
          }, this )
        );
      }else{
        this.$autoLocate.removeClass('active');
      }
    },

    //ZOOM
    _zoomIn: function() {
      this.setZoom(this.getZoom() + 1);
    },
    _zoomOut: function(){
      this.setZoom(this.getZoom() - 1);
    },
    getZoom: function(){
      return this.map.getZoom();
    },
    setZoom: function(zoom){
      this.map.setZoom(zoom);
    },

    setCenter: function() {
      var center = this.map.getCenter();
      var the_geom = JSON.stringify({
        'type': 'Point',
        'coordinates': [ center.lng(), center.lat() ]
      });
      this.model.set('the_geom',the_geom);
      this.$the_geom.val(this.model.get('the_geom'));
    },

    prettifyFilename: function (filename) {
      var file = filename.substring(0,filename.length - 4);
      return file.toLowerCase().replace(/ /g,"_");
    },

    render: function() {
      this.$the_geom = this.$('#story_the_geom');
      this.$autoLocate = this.$('#autoLocate');

      var the_geom = this.$the_geom.val()
      this.model.set('the_geom', the_geom);

      return this;
    }
  });

  return StoriesEditView;

});

<commit_msg>fix error notification for big pictures<commit_after>/**
 * The StoriesEdit view.
 */
define([
  'jquery',
  'backbone',
  'mps'
], function($,Backbone,mps) {

  'use strict';

  var config = {
    ZOOM: 3,
    MINZOOM: 3,
    MAXZOOM: 20,
    LAT: 15,
    LNG: 27,
    ISO: 'ALL',
    BASEMAP: 'grayscale',
    BASELAYER: 'loss',
    MONTHNAMES: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
    MONTHNAMES_SHORT: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
    QUARTERNAMES: ["JAN - MAR", "APR - JUN", "JUL - SEP", "OCT - DEC"],
    mapLoaded: false
  };

  config.MAPOPTIONS = {
    zoom: config.ZOOM,
    minZoom: config.MINZOOM,
    maxZoom: config.MAXZOOM,
    center: new google.maps.LatLng(config.LAT, config.LNG),
    mapTypeId: google.maps.MapTypeId.HYBRID,
    backgroundColor: '#99b3cc',
    disableDefaultUI: true,
  };

  config.OVERLAYSTYLES = {
    strokeWeight: 2,
    fillOpacity: 0.45,
    fillColor: "#FFF",
    strokeColor: "#A2BC28",
    editable: true,
    icon: new google.maps.MarkerImage(
      '/assets/icons/marker_exclamation.png',
      new google.maps.Size(36, 36), // size
      new google.maps.Point(0, 0), // offset
      new google.maps.Point(18, 18) // anchor
    )
  };

  config.MAPSTYLES = {};

  config.MAPSTYLES.grayscale = {
    type: 'style',
    style: [ { "featureType": "water" }, { "featureType": "transit", "stylers": [ { "saturation": -100 } ] }, { "featureType": "road", "stylers": [ { "saturation": -100 } ] }, { "featureType": "poi", "stylers": [ { "saturation": -100 } ] }, { "featureType": "landscape", "stylers": [ { "saturation": -100 } ] }, { "featureType": "administrative", "stylers": [ { "saturation": -100 } ] },{ "featureType": "poi.park", "elementType": "geometry", "stylers": [ { "visibility": 'off' } ] } ]
  }

  config.MAPSTYLES.terrain = {
    type: 'mapType',
    style: google.maps.MapTypeId.TERRAIN,
    title: "Terrain"
  }

  config.MAPSTYLES.satellite = {
    type: 'mapType',
    style: google.maps.MapTypeId.SATELLITE,
    title: "Satellite"
  }

  config.MAPSTYLES.roads = {
    type: 'mapType',
    style: google.maps.MapTypeId.HYBRID,
    title: "Roads"
  }

  config.MAPSTYLES.treeheight = {
    type: 'customMapType',
    style: new google.maps.ImageMapType({
      getTileUrl: function(ll, z) {
        var X = Math.abs(ll.x % (1 << z)); // wrap
        return window.gfw.config.GFW_API_HOST + "/gee/simple_green_coverage/" + z + "/" + X + "/" + ll.y + ".png";
      },
      tileSize: new google.maps.Size(256, 256),
      isPng: true,
      maxZoom: 17,
      name: "Forest Height",
      alt: "Global forest height"
    })
  }

  config.MAPSTYLES.landsat = [];

  for(var i = 1999; i < 2013; i++) {
    (function(year) {
      config.MAPSTYLES.landsat[i] = new google.maps.ImageMapType({
        getTileUrl: function(ll, z) {
          var X = Math.abs(ll.x % (1 << z));  // wrap
          return window.gfw.config.GFW_API_HOST + "/gee/landsat_composites/" + z + "/" + X + "/" + ll.y + ".png?year="+year;
        },
        tileSize: new google.maps.Size(256, 256),
        isPng: true,
        maxZoom: 17,
        name: "Landsat "+i
      });
    })(i);
  }

  var StoriesEditModel = Backbone.Model.extend({
    defaults: {
      the_geom: null
    }
  })

  var StoriesEditView = Backbone.View.extend({

    el: '#storiesEditView',

    events: {
      'click #zoomIn': '_zoomIn',
      'click #zoomOut': '_zoomOut',
      'click #autoLocate': '_autoLocate'
    },

    initialize: function() {
      if (!this.$el.length) {
        return
      }

      this.model = new StoriesEditModel();

      this.uploadsIds = [];
      this.filesAdded = 0;

      this._initViews();
      this._initBindings();

      this.render();
    },

    _initBindings: function() {
      var that = this;

      $('.upload_picture').on('click', function(e) {
        e.preventDefault();

        $('#fileupload').click();
      });

      $('#fileupload').fileupload({
          url: '/media/upload',
          dataType: 'json',
          autoUpload: true,
          acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
          maxFileSize: 4000000, // 4 MB
          // Enable image resizing, except for Android and Opera,
          // which actually support image resizing, but fail to
          // send Blob objects via XHR requests:
          disableImageResize: /Android(?!.*Chrome)|Opera/
            .test(window.navigator.userAgent),
          previewMaxWidth: 132,
          previewMaxHeight: 76,
          previewCrop: true,
          timeout: 3600000
      }).on('fileuploadadd', function (e, data) {
        data.context = $('
').appendTo('#files');

        that.filesAdded += _.size(data.files);
        _.each(data.files, function(file) {
          if (file && file.size > 4000000) {
            mps.publish('Notification/open', ['notif-limit-exceed']);
            return;
          } else {
            var filename = that.prettifyFilename(file.name);
            var $thumbnail = $("

"+ file.name +"

<svg><use xlink:href='#shape-spinner'></use></svg>

");
            $('.thumbnails').append($thumbnail);
            $thumbnail.fadeIn(250);

            var $submitButton = $("form input[type='submit']");
            $submitButton.addClass('disabled');
            $submitButton.attr('disabled', 'disabled');
            $submitButton.val('Please wait...');
          }
        });

        // data.submit();
      }).on('fileuploaddone', function (e, data) {
        var files = [data.result];

        $.each(files, function (index, file) {
          that.filesAdded--;
          that.uploadsIds.push(file.basename);

          var url = file.url.replace('https', 'http');
          var $thumb = $("

<svg><use xlink:href='#shape-close'></use></svg>
");

          var filename = that.prettifyFilename(file.basename).substring(45);

          $(".thumbnail[data-name='"+filename+"']").fadeOut(250, function() {
            $(this).remove();

            $(".thumbnails").append($thumb);
            $thumb.fadeIn(250);
          });

          $thumb.find('.destroy').on('click', function(e) {
            e.preventDefault();

            var confirmation = confirm('Are you sure?')

            if (confirmation == true) {
              that.uploadsIds = _.without(that.uploadsIds, file.basename);
              $("#story_uploads_ids").val(that.uploadsIds.join(","));

              $thumb.fadeOut(250, function() {
                $thumb.remove();
              });
            }
          });
        });

        if (that.filesAdded <= 0) {
          var $submitButton = $("form input[type='submit']");
          $submitButton.val('Submit story');
          $submitButton.removeClass('disabled');
          $submitButton.attr('disabled', false);
        }

        $('#story_uploads_ids').val(that.uploadsIds.join(','));
        ga('send', 'event', 'Stories', 'New story', 'submit');
      }).on('fileuploadfail', function (e, data){
        mps.publish('Notification/open', ['upload-error-server']);
        var $submitButton = $("form input[type='submit']");
        $submitButton.val('Submit story');
        $submitButton.removeClass('disabled');
        $submitButton.attr('disabled', false);
      });
    },

    _initViews: function() {
      var that = this;
      var $searchInput = $('.map-search-input');

      // Load map
      this.map = new google.maps.Map(document.getElementById('stories_map'),config.MAPOPTIONS);

      // Listen to map loaded
      google.maps.event.addListenerOnce(this.map, 'idle', _.bind(function(){
        this._autoLocate();
      }, this ));

      // Set autocomplete search input
      this.autocomplete = new google.maps.places.Autocomplete($searchInput[0], {types: ['geocode']});

      // Listen to selected areas (search)
      google.maps.event.addListener(this.autocomplete, 'place_changed', _.bind(function() {
        var place = this.autocomplete.getPlace();
        if (place && place.geometry && place.geometry.viewport) {
          this.map.fitBounds(place.geometry.viewport)
        }
        if (place && place.geometry && place.geometry.location && !place.geometry.viewport) {
          var index = [];
          for (var x in place.geometry.location) {
             index.push(x);
          }
          this.map.setCenter(new google.maps.LatLng(place.geometry.location[index[0]], place.geometry.location[index[1]]));
        }
      }, this ));

      google.maps.event.addDomListener($searchInput[0], 'keydown', function(e) {
        if (e.keyCode == 13) {
          e.preventDefault();
        }
      });

      // Listen to any change on center position
      google.maps.event.addListener(this.map, 'zoom_changed',
        _.bind(function() {
          this.setCenter();
        }, this)
      );
      google.maps.event.addListener(this.map, 'dragend',
        _.bind(function() {
          this.setCenter();
      }, this));

    },

    _autoLocate: function(e){
      this.$autoLocate.addClass('active');
      if(navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(
          _.bind(function(position) {
            this.$autoLocate.removeClass('active');
            var pos = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
            this.map.panTo(pos);
            this.setCenter();
            this.setZoom(18);
          }, this ),
          _.bind(function() {
            this.$autoLocate.removeClass('active');
            mps.publish('Notification/open', ['notif-enable-location']);
          }, this )
        );
      }else{
        this.$autoLocate.removeClass('active');
      }
    },

    //ZOOM
    _zoomIn: function() {
      this.setZoom(this.getZoom() + 1);
    },
    _zoomOut: function(){
      this.setZoom(this.getZoom() - 1);
    },
    getZoom: function(){
      return this.map.getZoom();
    },
    setZoom: function(zoom){
      this.map.setZoom(zoom);
    },

    setCenter: function() {
      var center = this.map.getCenter();
      var the_geom = JSON.stringify({
        'type': 'Point',
        'coordinates': [ center.lng(), center.lat() ]
      });
      this.model.set('the_geom',the_geom);
      this.$the_geom.val(this.model.get('the_geom'));
    },

    prettifyFilename: function (filename) {
      var file = filename.substring(0,filename.length - 4);
      return file.toLowerCase().replace(/ /g,"_");
    },

    render: function() {
      this.$the_geom = this.$('#story_the_geom');
      this.$autoLocate = this.$('#autoLocate');

      var the_geom = this.$the_geom.val()
      this.model.set('the_geom', the_geom);

      return this;
    }
  });

  return StoriesEditView;

});

<|endoftext|>


<commit_before>(function(){
  var $searchButton,
      $originInput,
      $destinationInput,
      $clearButton,
      $metricsContent,
      options = {
          modes: ['walking', 'biking', 'transit', 'driving'],
          metrics: ['cost', 'duration', 'calories', 'emissions']
      },
      metricsEjs = new EJS({url: 'views/metrics.ejs'}),
      log = function(toLog) {
        if (window.console && window.console.log) {
          window.console.log(toLog);
        }
      };

  var clearInputs = function() {
    $originInput.val('');
    $destinationInput.val('');
  };

  var calculate = function (origin, destination) {
    $.mobile.pageLoading();

    $.ajax({
      url: 'info_for_route_bing',
      dataType: 'json',
      data: {
        origin: origin,
        destination: destination
      },
      success: function(data, textStatus, jqXHR) {
        var html = metricsEjs.render({
          modes: options.modes,
          metrics: options.metrics,
          data: data
        });

        $metricsContent.html(html);

        $.mobile.changePage("#home", "slideup", true, false);
      },
      error: function(jqXHR, textStatus, errorThrown) {
        log(errorThrown);
      },
      complete: function() {
        $.mobile.pageLoading(true);
      }
    });
  };

  var bindEvents = function(){
    $searchButton.tap(function(){
      calculate($originInput.val(), $destinationInput.val());
    });

    $clearButton.tap(clearInputs);
  };

  $('#home').live('pagecreate',function(event){
    //Cache vars
    $searchButton = $('#search-button');
    $originInput = $('#origin');
    $destinationInput = $('#destination');
    $clearButton = $('#clear-button');
    $metricsContent = $('#metrics-content');

    bindEvents();
  });

  $('#home').live('pagecreate',function(event) {
    $.mobile.changePage("#search", "slidedown");
  });

})();<commit_msg>Fix initial search dialog transition.<commit_after>(function(){
  var $searchButton,
      $originInput,
      $destinationInput,
      $clearButton,
      $metricsContent,
      options = {
          modes: ['walking', 'biking', 'transit', 'driving'],
          metrics: ['cost', 'duration', 'calories', 'emissions']
      },
      metricsEjs = new EJS({url: 'views/metrics.ejs'}),
      log = function(toLog) {
        if (window.console && window.console.log) {
          window.console.log(toLog);
        }
      };

  var clearInputs = function() {
    $originInput.val('');
    $destinationInput.val('');
  };

  var formatResults = function(data) {
    return data.results;
  };

  var calculate = function(origin, destination) {
    $.mobile.pageLoading();

    $.ajax({
      url: 'info_for_route_bing',
      dataType: 'json',
      data: {
        origin: origin,
        destination: destination
      },
      success: function(data, textStatus, jqXHR) {
        var html = metricsEjs.render({
          modes: options.modes,
          metrics: options.metrics,
          results: formatResults(data)
        });

        $metricsContent.html(html);

        $.mobile.changePage("#home", "slideup", true, false);
      },
      error: function(jqXHR, textStatus, errorThrown) {
        log(errorThrown);
      },
      complete: function() {
        $.mobile.pageLoading(true);
      }
    });
  };

  var bindEvents = function(){
    $searchButton.tap(function(){
      calculate($originInput.val(), $destinationInput.val());
    });

    $clearButton.tap(clearInputs);
  };

  $('#home').live('pagecreate',function(event){
    //Cache vars
    $searchButton = $('#search-button');
    $originInput = $('#origin');
    $destinationInput = $('#destination');
    $clearButton = $('#clear-button');
    $metricsContent = $('#metrics-content');

    bindEvents();
  });

  $('#home').live('pagecreate',function(event) {
    $.mobile.changePage("#search", "slideup");
  });
})();<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');

const TAB_WIDTH = 8;

class Terminal {
  constructor() {
    this.state = {
      lastRefresh: 0,
      lines: [''],
      lineWrap: 256,
      maxLines: 2048,
      pointerLine: 0,
      pointerColumn: 0,
      refreshDelayMillis: 64,
      refreshQueued: null,
      trimCount: 64
    };

    this.queue = this.queue.bind(this);
  }

  queue(cb){
    this.state.lastRefresh = Date.now();
    this.state.refreshQueued = null;
    cb();
  }

  clearAll(){
    const { refreshQueued } = this.state;

    this.state.lines = [''];
    this.state.pointerLine = 0;
    this.state.pointerColumn = 0;

    if(refreshQueued != null){
      clearInterval(refreshQueued);
      this.state.refreshQueued = null;
    }
    this.state.lastRefresh = 0;
  }

  refreshBuffer(msg, cb) {
    const { lastRefresh, refreshDelayMillis, refreshQueued } = this.state;

    this._update(msg);

    if(refreshQueued != null){
      return;
    }

    // TODO: remove zalgo
    if(lastRefresh < Date.now() - refreshDelayMillis){
      this.queue(cb);
    } else {
      this.state.refreshQueued = setTimeout(this.queue, refreshDelayMillis, cb);
    }
  }

  _update(events) {
    if(Array.isArray(events)){
      _.forEach(events, this.processEvent, this);
    }else{
      this.processEvent(events);
    }
  }

  setCursorPosition(line, col){
    const { lines } = this.state;
    for(var ix = lines.length; ix <= line; ix++){
      lines[ix] = '';
    }
    this.state.pointerLine = Math.max(0, line);
    this.state.pointerColumn = Math.min(255, Math.max(0, col));
  }

  backspace(){
    const { lines, pointerLine, pointerColumn } = this.state;
    if(pointerColumn > 0){
      const targetLine = lines[pointerLine];
      if(pointerColumn < targetLine.length){
        lines[pointerLine] = targetLine.slice(0, pointerColumn - 1) + targetLine.slice(pointerColumn);
      } else {
        lines[pointerLine] = targetLine.slice(0, pointerColumn - 1);
      }
      this.state.pointerColumn = pointerColumn - 1;
    } else {
      const prevLineNum = Math.max(0, pointerLine - 1);
      const prevLine = lines[prevLineNum] || '';
      this.state.pointerLine = prevLineNum;
      this.state.pointerColumn = prevLine.length;
    }
  }

  clearEol(){
    const { lines, pointerLine, pointerColumn } = this.state;
    const line = lines[pointerLine] || '';
    if(pointerColumn < line.length){
      lines[pointerLine] = line.slice(0, pointerColumn);
    }
  }

  clearBelow(){
    const { lines, pointerLine } = this.state;
    const newLines = lines.slice(0, Math.min(pointerLine + 1, lines.length));
    this.state.lines = newLines;
  }

  tab(){
    const { pointerColumn } = this.state;
    const tabColumn = Math.floor(pointerColumn / TAB_WIDTH);
    this.state.pointerColumn = (tabColumn + 1) * TAB_WIDTH;
  }

  processEvent(evt){
    const { pointerLine, pointerColumn } = this.state;
    switch(evt.type){
      case 'text':
        this.addText(evt.data);
        break;
      case 'linefeed':
        this.setCursorPosition(pointerLine + 1, 0);
        break;
      case 'cursor-home':
        this.setCursorPosition(0, 0);
        break;
      case 'cursor-position':
        this.setCursorPosition(evt.data[1], evt.data[0]);
        break;
      case 'cursor-position-x':
        this.setCursorPosition(pointerLine, evt.data[0]);
        break;
      case 'cursor-position-y':
        this.setCursorPosition(evt.data[0], pointerColumn);
        break;
      case 'cursor-left':
        this.setCursorPosition(pointerLine, pointerColumn - 1);
        break;
      case 'cursor-right':
        this.setCursorPosition(pointerLine, pointerColumn + 1);
        break;
      case 'cursor-up':
        this.setCursorPosition(pointerLine - 1, pointerColumn);
        break;
      case 'cursor-down':
        this.setCursorPosition(pointerLine + 1, pointerColumn);
        break;
      case 'clear-screen':
        this.clearAll();
        break;
      case 'backspace':
        this.backspace();
        break;
      case 'clear-eol':
        this.clearEol();
        break;
      case 'clear-below':
        this.clearBelow();
        break;
      case 'tab':
        this.tab();
        break;
      default:
        console.log('Not yet implemented:', evt);
        break;
    }
  }

  addText(data){
    const { lines, lineWrap, pointerLine, pointerColumn, trimCount, maxLines } = this.state;

    var newPointerColumn = pointerColumn;
    var newPointerLine = pointerLine;
    var newText = data;

    while(newText.length > 0){
      var oldLine = lines[newPointerLine] || '';
      var initial = _.padRight(oldLine.slice(0, newPointerColumn), newPointerColumn);

      var newData = newText.slice(0, lineWrap - initial.length);

      var remainder = oldLine.slice(newData.length + initial.length);

      var leftOver = newText.slice(lineWrap - initial.length);

      lines[newPointerLine] = initial + newData + remainder;

      newText = leftOver;
      if(newText.length > 0){
        newPointerLine++;
        newPointerColumn = 0;
      }else{
        newPointerColumn = initial.length + newData.length;
      }
    }

    if(lines.length > maxLines){
      const newLines = lines.slice(trimCount);
      this.state.lines = newLines;
      this.state.pointerLine = Math.max(0, newPointerLine - trimCount);
      this.state.pointerColumn = newPointerColumn;
    } else {
      this.state.pointerLine = newPointerLine;
      this.state.pointerColumn = newPointerColumn;
    }
  }

  getLines(){
    return this.state.lines;
  }

}

module.exports = Terminal;
<commit_msg>Rename variables, misc cleanup.<commit_after>'use strict';

const _ = require('lodash');

const TAB_WIDTH = 8;

class Terminal {
  constructor() {
    this.state = {
      lastRefresh: 0,
      lines: [''],
      lineWrap: 256,
      maxLines: 2048,
      pointerLine: 0,
      pointerColumn: 0,
      refreshDelayMillis: 64,
      refreshQueued: null,
      trimCount: 64
    };

    this.queue = this.queue.bind(this);
  }

  queue(cb){
    this.state.lastRefresh = Date.now();
    this.state.refreshQueued = null;
    cb();
  }

  clearAll(){
    const { refreshQueued } = this.state;

    this.state.lines = [''];
    this.state.pointerLine = 0;
    this.state.pointerColumn = 0;

    if(refreshQueued != null){
      clearInterval(refreshQueued);
      this.state.refreshQueued = null;
    }
    this.state.lastRefresh = 0;
  }

  refreshBuffer(msg, cb) {
    const { lastRefresh, refreshDelayMillis, refreshQueued } = this.state;

    this._update(msg);

    if(refreshQueued != null){
      return;
    }

    // TODO: remove zalgo
    if(lastRefresh < Date.now() - refreshDelayMillis){
      this.queue(cb);
    } else {
      this.state.refreshQueued = setTimeout(this.queue, refreshDelayMillis, cb);
    }
  }

  _update(events) {
    if(Array.isArray(events)){
      _.forEach(events, this.processEvent, this);
    }else{
      this.processEvent(events);
    }
  }

  setCursorPosition(line, col){
    const { lines } = this.state;
    for(var ix = lines.length; ix <= line; ix++){
      lines[ix] = '';
    }
    this.state.pointerLine = Math.max(0, line);
    this.state.pointerColumn = Math.min(255, Math.max(0, col));
  }

  backspace(){
    const { lines, pointerLine, pointerColumn } = this.state;
    if(pointerColumn > 0){
      const targetLine = lines[pointerLine];
      if(pointerColumn < targetLine.length){
        lines[pointerLine] = targetLine.slice(0, pointerColumn - 1) + targetLine.slice(pointerColumn);
      } else {
        lines[pointerLine] = targetLine.slice(0, pointerColumn - 1);
      }
      this.state.pointerColumn = pointerColumn - 1;
    } else {
      const prevLineNum = Math.max(0, pointerLine - 1);
      const prevLine = lines[prevLineNum] || '';
      this.state.pointerLine = prevLineNum;
      this.state.pointerColumn = prevLine.length;
    }
  }

  clearEol(){
    const { lines, pointerLine, pointerColumn } = this.state;
    const line = lines[pointerLine] || '';
    if(pointerColumn < line.length){
      lines[pointerLine] = line.slice(0, pointerColumn);
    }
  }

  clearBelow(){
    const { lines, pointerLine } = this.state;
    const newLines = lines.slice(0, Math.min(pointerLine + 1, lines.length));
    this.state.lines = newLines;
  }

  tab(){
    const { pointerColumn } = this.state;
    const tabColumn = Math.floor(pointerColumn / TAB_WIDTH);
    this.state.pointerColumn = (tabColumn + 1) * TAB_WIDTH;
  }

  processEvent(evt){
    const { pointerLine, pointerColumn } = this.state;
    switch(evt.type){
      case 'text':
        this.addText(evt.data);
        break;
      case 'linefeed':
        this.setCursorPosition(pointerLine + 1, 0);
        break;
      case 'cursor-home':
        this.setCursorPosition(0, 0);
        break;
      case 'cursor-position':
        this.setCursorPosition(evt.data[1], evt.data[0]);
        break;
      case 'cursor-position-x':
        this.setCursorPosition(pointerLine, evt.data[0]);
        break;
      case 'cursor-position-y':
        this.setCursorPosition(evt.data[0], pointerColumn);
        break;
      case 'cursor-left':
        this.setCursorPosition(pointerLine, pointerColumn - 1);
        break;
      case 'cursor-right':
        this.setCursorPosition(pointerLine, pointerColumn + 1);
        break;
      case 'cursor-up':
        this.setCursorPosition(pointerLine - 1, pointerColumn);
        break;
      case 'cursor-down':
        this.setCursorPosition(pointerLine + 1, pointerColumn);
        break;
      case 'clear-screen':
        this.clearAll();
        break;
      case 'backspace':
        this.backspace();
        break;
      case 'clear-eol':
        this.clearEol();
        break;
      case 'clear-below':
        this.clearBelow();
        break;
      case 'tab':
        this.tab();
        break;
      default:
        console.log('Not yet implemented:', evt);
        break;
    }
  }

  addText(newText){
    const { lines, lineWrap, trimCount, maxLines } = this.state;

    let { pointerColumn, pointerLine } = this.state;

    while(newText.length > 0){
      let oldLine = lines[pointerLine] || '';
      let initial = _.padRight(oldLine.slice(0, pointerColumn), pointerColumn);

      let insert = newText.slice(0, Math.max(lineWrap - initial.length, 0));

      let remainder = oldLine.slice(insert.length + initial.length);

      let leftOver = newText.slice(insert.length);

      lines[pointerLine] = initial + insert + remainder;

      newText = leftOver;
      if(newText.length > 0){
        pointerLine++;
        pointerColumn = 0;
      }else{
        pointerColumn = initial.length + insert.length;
      }
    }

    if(lines.length > maxLines){
      const newLines = lines.slice(trimCount);
      this.state.lines = newLines;
      this.state.pointerLine = Math.max(0, pointerLine - trimCount);
      this.state.pointerColumn = pointerColumn;
    } else {
      this.state.pointerLine = pointerLine;
      this.state.pointerColumn = pointerColumn;
    }
  }

  getLines(){
    return this.state.lines;
  }

}

module.exports = Terminal;
<|endoftext|>


<commit_before><commit_msg>Change command signal to comma<commit_after><|endoftext|>


<commit_before>import 'babel/polyfill';

import throttle from 'lodash/function/throttle';
import forOwn from 'lodash/object/forOwn';

import {ClientReactApp} from 'horse-react';
import attachFastClick from 'fastclick';
import mixin from '../../src/app-mixin';
import querystring from 'querystring';
import superagent from 'superagent';

var App = mixin(ClientReactApp);

import config from '../../src/config';
import constants from '../../src/constants';
import cookies from 'cookies-js';
import getTimes from '../../src/lib/timing';
import globals from '../../src/globals';
import randomBySeed from '../../src/lib/randomBySeed';
import routes from '../../src/routes';
import Utils from '../../src/lib/danehansen/utils/Utils';

import trackingEvents from './trackingEvents';
import TweenLite from 'gsap';

var _lastWinWidth = 0;

function loadShim() {
  var head = document.head || document.getElementsByTagName('head')[0];

  var shimScript = document.createElement('script');
  shimScript.type = 'text\/javascript';
  shimScript.onload = function() {
    initialize(false);
  }

  head.appendChild(shimScript, document.currentScript);

  shimScript.src = window.bootstrap.assetPath + '/js/es5-shims.js';
}

function onLoad(fn) {
  if (document.readyState !== 'complete' && document.readyState !== 'interactive') {
    window.addEventListener('DOMContentLoaded', fn);
  } else {
    fn();
  }
}

function redirect(status, path) {
  if ((typeof status === 'string') && !path) {
    path = status;
  }

  if (path.indexOf('/login') > -1 || path.indexOf('/register') > -1 ) {
    window.location = path;
  } else {
    this.redirect(path);
  }
}

// A few es5 sanity checks
if (!Object.create || !Array.prototype.map || !Object.freeze) {
  onLoad(loadShim);
} else {
  onLoad(function() {
    initialize(true);
  });
}

var referrer = document.referrer;

function modifyContext (ctx) {
  ctx.loid = this.getState('loid');
  ctx.loidcreated = this.getState('loidcreated');
  ctx.token = this.getState('token');
  ctx.user = this.getState('user');
  ctx.useCache = true;
  ctx.experiments = this.getState('experiments');
  ctx.headers.referer = referrer;

  // Is set with a client-side cookie, sometimes hitting 'back' doesn't
  // restore proper state
  ctx.compact = (cookies.get('compact') || '').toString() === 'true';

  ctx.redirect = redirect.bind(this);

  return ctx;
}

var $title = document.getElementsByTagName('title')[0];

function setTitle(props={}) {
  if (props.title) {
    if ($title.textContent) {
      $title.textContent = props.title;
    } else if ($title.innerText) {
      $title.innerText = props.title;
    }
  }
}

function refreshToken (app, tries=0) {
  if (tries >= 3) {
    window.location = '/logout';
  }

  superagent
    .get('/oauth2/refresh')
    .end(function(err, res) {
      if (err) {
        return window.setTimeout(function() {
            refreshToken(app, tries + 1);
        }, 1000 * 10);
      }

      var token = res.body;
      app.setState('token', token.token);
      app.setState('tokenExpires', token.tokenExpires);

      var now = new Date();
      var expires = new Date(token.tokenExpires);

      window.setTimeout(function() {
        refreshToken(app);
      }, (expires - now) * .9);
    });
}

function findLinkParent(el) {
  if (el.parentNode) {
    if (el.parentNode.tagName === 'A') {
      return el.parentNode;
    }

    return findLinkParent(el.parentNode);
  }
}

function initialize(bindLinks) {
  var plugin;
  var p;

  config.mountPoint = document.getElementById('app-container');
  config.touch = true;

  forOwn(config, function(val, key) {
    if (bootstrap[key]) {
      config[key] = bootstrap[key];
    }
  });

  config.seed = window.bootstrap.seed || Math.random();

  globals().touch = Utils.touch();
  globals().random = randomBySeed(config.seed);

  var app = new App(config);
  globals().app = app;

  app.emitter.setMaxListeners(30);

  if (app.getState('token')) {
    var now = new Date();
    var expires = new Date(app.getState('tokenExpires'));

    var refreshMS = (expires - now);

    // refresh a little before it expires, to be safe
    refreshMS *= .90;

    // if it's within a minute, refresh now
    refreshMS = Math.max(refreshMS - (1000 * 60), 0);

    window.setTimeout(function() {
      refreshToken(app);
    }, refreshMS);
  };

  app.router.get('/oauth2/login', function * () {
    window.location = '/oauth2/login';
  });

  routes(app);

  modifyContext = modifyContext.bind(app);
  app.modifyContext = modifyContext;

  var history = window.history || window.location.history;
  app.pushState = (data, title, url) => {
    if (history) {
      history.pushState(data, title, url);
    }
  };

  app.redirect = function(url) {
    app.pushState(null, null, url);

    // Set to the browser's interpretation of the current name (to make
    // relative paths easier), and send in the old url.
    app.render(app.fullPathName(), false, modifyContext).then(function(props) {
      setTitle(props);
    });
  }

  var scrollCache = {};

  var initialUrl = app.fullPathName();

  function attachEvents() {
    attachFastClick(document.body);

    if(history && bindLinks) {
      var $body = document.body;

      $body.addEventListener('click', function(e) {
        var $link = e.target;

        if ($link.tagName !== 'A') {
          $link = findLinkParent($link);

          if (!$link) {
            return;
          }
        }

        var href = $link.getAttribute('href');
        var currentUrl = app.fullPathName();

        // If it has a target=_blank, or an 'external' data attribute, or it's
        // an absolute url, let the browser route rather than forcing a capture.
        if (
          ($link.target === '_blank' || $link.dataset.noRoute === 'true') ||
          href.indexOf('//') > -1
        ) {
          return;
        }

        e.preventDefault();

        scrollCache[currentUrl] = window.scrollY;

        if (href.indexOf('#') === 0) {
          return;
        }

        initialUrl = href;

        app.pushState(null, null, href);

        // Set to the browser's interpretation of the current name (to make
        // relative paths easier), and send in the old url.
        app.render(app.fullPathName(), false, modifyContext).then(function(props) {
          setTitle(props);
        });
      });

      window.addEventListener('popstate', function(e) {
        var href = app.fullPathName();
        // Work around some browsers firing popstate on initial load
        if (href !== initialUrl) {
          scrollCache[initialUrl] = window.scrollY;

          app.render(href, false, modifyContext).then(function(props) {
            if(scrollCache[href]) {
              $body.scrollTop = scrollCache[href];
            }

            setTitle(props);
          });

          initialUrl = href;
        }
      });
    }
  }

  // Don't re-render tracking pixel on first load. App reads from state
  // (bootstrap) on first load, so override state, and then set the proper
  // config value after render.
  app.render(app.state.url, true, modifyContext).then(function() {
    attachEvents();
    referrer = document.location.href;
  });

  app.on('route:desktop', function(route) {
    let options = {};

    let date = new Date();
    date.setFullYear(date.getFullYear() + 2);
    options.expire = date;

    if (window.location.host.indexOf('localhost') === -1) {
      var domain = '.' + bootstrap.reddit.match(/https?:\/\/(.+)/)[1].split('.').splice(1,2).join('.');
      options.domain = domain;
    }

    cookies.set('__cf_mob_redir', '0', options);

    if (route.indexOf('?') === -1) {
      route += '?ref_source=mweb';
    } else {
      route += '&ref_source=mweb';
    }

    window.location = `https://www.reddit.com${route}`;
  });

  app.on(constants.COMPACT_TOGGLE, function(compact) {
    app.setState('compact', compact);
  });

  window.addEventListener('scroll', throttle(function() {
      app.emit(constants.SCROLL);
    }.bind(app), 100));

  globals().winWidth = window.innerWidth;
  window.addEventListener('resize', throttle(function(e) {
    // Prevent resize from firing when chrome shows/hides nav bar
    globals().winWidth = window.innerWidth;
    if (globals().winWidth !== _lastWinWidth) {
      _lastWinWidth = globals().winWidth;
      app.emit(constants.RESIZE);
    }
  }.bind(app), 100));

  // Send the timings during the next cycle.
  setTimeout(function() {
    if (window.bootstrap.actionName) {
      var timings = Object.assign({
        actionName: 'm.server.' + window.bootstrap.actionName,
      }, getTimes());

      var $csrf = document.getElementById('csrf-token-meta-tag');

      superagent
        .post('/timings')
        .send({
          rum: timings,
          _csrf: $csrf.content,
        })
        .end(function(){});
    }
  }, 1);

  if (window.bootstrap.propertyId) {
    trackingEvents(app);
  }
}

module.exports = initialize;
<commit_msg>Fix attempted routing of javascript hrefs<commit_after>import 'babel/polyfill';

import throttle from 'lodash/function/throttle';
import forOwn from 'lodash/object/forOwn';

import {ClientReactApp} from 'horse-react';
import attachFastClick from 'fastclick';
import mixin from '../../src/app-mixin';
import querystring from 'querystring';
import superagent from 'superagent';

var App = mixin(ClientReactApp);

import config from '../../src/config';
import constants from '../../src/constants';
import cookies from 'cookies-js';
import getTimes from '../../src/lib/timing';
import globals from '../../src/globals';
import randomBySeed from '../../src/lib/randomBySeed';
import routes from '../../src/routes';
import Utils from '../../src/lib/danehansen/utils/Utils';

import trackingEvents from './trackingEvents';
import TweenLite from 'gsap';

var _lastWinWidth = 0;

function loadShim() {
  var head = document.head || document.getElementsByTagName('head')[0];

  var shimScript = document.createElement('script');
  shimScript.type = 'text\/javascript';
  shimScript.onload = function() {
    initialize(false);
  }

  head.appendChild(shimScript, document.currentScript);

  shimScript.src = window.bootstrap.assetPath + '/js/es5-shims.js';
}

function onLoad(fn) {
  if (document.readyState !== 'complete' && document.readyState !== 'interactive') {
    window.addEventListener('DOMContentLoaded', fn);
  } else {
    fn();
  }
}

function redirect(status, path) {
  if ((typeof status === 'string') && !path) {
    path = status;
  }

  if (path.indexOf('/login') > -1 || path.indexOf('/register') > -1 ) {
    window.location = path;
  } else {
    this.redirect(path);
  }
}

// A few es5 sanity checks
if (!Object.create || !Array.prototype.map || !Object.freeze) {
  onLoad(loadShim);
} else {
  onLoad(function() {
    initialize(true);
  });
}

var referrer = document.referrer;

function modifyContext (ctx) {
  ctx.loid = this.getState('loid');
  ctx.loidcreated = this.getState('loidcreated');
  ctx.token = this.getState('token');
  ctx.user = this.getState('user');
  ctx.useCache = true;
  ctx.experiments = this.getState('experiments');
  ctx.headers.referer = referrer;

  // Is set with a client-side cookie, sometimes hitting 'back' doesn't
  // restore proper state
  ctx.compact = (cookies.get('compact') || '').toString() === 'true';

  ctx.redirect = redirect.bind(this);

  return ctx;
}

var $title = document.getElementsByTagName('title')[0];

function setTitle(props={}) {
  if (props.title) {
    if ($title.textContent) {
      $title.textContent = props.title;
    } else if ($title.innerText) {
      $title.innerText = props.title;
    }
  }
}

function refreshToken (app, tries=0) {
  if (tries >= 3) {
    window.location = '/logout';
  }

  superagent
    .get('/oauth2/refresh')
    .end(function(err, res) {
      if (err) {
        return window.setTimeout(function() {
            refreshToken(app, tries + 1);
        }, 1000 * 10);
      }

      var token = res.body;
      app.setState('token', token.token);
      app.setState('tokenExpires', token.tokenExpires);

      var now = new Date();
      var expires = new Date(token.tokenExpires);

      window.setTimeout(function() {
        refreshToken(app);
      }, (expires - now) * .9);
    });
}

function findLinkParent(el) {
  if (el.parentNode) {
    if (el.parentNode.tagName === 'A') {
      return el.parentNode;
    }

    return findLinkParent(el.parentNode);
  }
}

function initialize(bindLinks) {
  var plugin;
  var p;

  config.mountPoint = document.getElementById('app-container');
  config.touch = true;

  forOwn(config, function(val, key) {
    if (bootstrap[key]) {
      config[key] = bootstrap[key];
    }
  });

  config.seed = window.bootstrap.seed || Math.random();

  globals().touch = Utils.touch();
  globals().random = randomBySeed(config.seed);

  var app = new App(config);
  globals().app = app;

  app.emitter.setMaxListeners(30);

  if (app.getState('token')) {
    var now = new Date();
    var expires = new Date(app.getState('tokenExpires'));

    var refreshMS = (expires - now);

    // refresh a little before it expires, to be safe
    refreshMS *= .90;

    // if it's within a minute, refresh now
    refreshMS = Math.max(refreshMS - (1000 * 60), 0);

    window.setTimeout(function() {
      refreshToken(app);
    }, refreshMS);
  };

  app.router.get('/oauth2/login', function * () {
    window.location = '/oauth2/login';
  });

  routes(app);

  modifyContext = modifyContext.bind(app);
  app.modifyContext = modifyContext;

  var history = window.history || window.location.history;
  app.pushState = (data, title, url) => {
    if (history) {
      history.pushState(data, title, url);
    }
  };

  app.redirect = function(url) {
    app.pushState(null, null, url);

    // Set to the browser's interpretation of the current name (to make
    // relative paths easier), and send in the old url.
    app.render(app.fullPathName(), false, modifyContext).then(function(props) {
      setTitle(props);
    });
  }

  var scrollCache = {};

  var initialUrl = app.fullPathName();

  function attachEvents() {
    attachFastClick(document.body);

    if(history && bindLinks) {
      var $body = document.body;

      $body.addEventListener('click', function(e) {
        var $link = e.target;

        if ($link.tagName !== 'A') {
          $link = findLinkParent($link);

          if (!$link) {
            return;
          }
        }

        var href = $link.getAttribute('href');
        var currentUrl = app.fullPathName();

        // If it has a target=_blank, or an 'external' data attribute, or it's
        // an absolute url, let the browser route rather than forcing a capture.
        if (
          ($link.target === '_blank' || $link.dataset.noRoute === 'true') ||
          href.indexOf('//') > -1
        ) {
          return;
        }

        // If the href contains script ignore it
        if (/^javascript:/.test(href)) {
          return;
        }

        e.preventDefault();

        scrollCache[currentUrl] = window.scrollY;

        if (href.indexOf('#') === 0) {
          return;
        }

        initialUrl = href;

        app.pushState(null, null, href);

        // Set to the browser's interpretation of the current name (to make
        // relative paths easier), and send in the old url.
        app.render(app.fullPathName(), false, modifyContext).then(function(props) {
          setTitle(props);
        });
      });

      window.addEventListener('popstate', function(e) {
        var href = app.fullPathName();
        // Work around some browsers firing popstate on initial load
        if (href !== initialUrl) {
          scrollCache[initialUrl] = window.scrollY;

          app.render(href, false, modifyContext).then(function(props) {
            if(scrollCache[href]) {
              $body.scrollTop = scrollCache[href];
            }

            setTitle(props);
          });

          initialUrl = href;
        }
      });
    }
  }

  // Don't re-render tracking pixel on first load. App reads from state
  // (bootstrap) on first load, so override state, and then set the proper
  // config value after render.
  app.render(app.state.url, true, modifyContext).then(function() {
    attachEvents();
    referrer = document.location.href;
  });

  app.on('route:desktop', function(route) {
    let options = {};

    let date = new Date();
    date.setFullYear(date.getFullYear() + 2);
    options.expire = date;

    if (window.location.host.indexOf('localhost') === -1) {
      var domain = '.' + bootstrap.reddit.match(/https?:\/\/(.+)/)[1].split('.').splice(1,2).join('.');
      options.domain = domain;
    }

    cookies.set('__cf_mob_redir', '0', options);

    if (route.indexOf('?') === -1) {
      route += '?ref_source=mweb';
    } else {
      route += '&ref_source=mweb';
    }

    window.location = `https://www.reddit.com${route}`;
  });

  app.on(constants.COMPACT_TOGGLE, function(compact) {
    app.setState('compact', compact);
  });

  window.addEventListener('scroll', throttle(function() {
      app.emit(constants.SCROLL);
    }.bind(app), 100));

  globals().winWidth = window.innerWidth;
  window.addEventListener('resize', throttle(function(e) {
    // Prevent resize from firing when chrome shows/hides nav bar
    globals().winWidth = window.innerWidth;
    if (globals().winWidth !== _lastWinWidth) {
      _lastWinWidth = globals().winWidth;
      app.emit(constants.RESIZE);
    }
  }.bind(app), 100));

  // Send the timings during the next cycle.
  setTimeout(function() {
    if (window.bootstrap.actionName) {
      var timings = Object.assign({
        actionName: 'm.server.' + window.bootstrap.actionName,
      }, getTimes());

      var $csrf = document.getElementById('csrf-token-meta-tag');

      superagent
        .post('/timings')
        .send({
          rum: timings,
          _csrf: $csrf.content,
        })
        .end(function(){});
    }
  }, 1);

  if (window.bootstrap.propertyId) {
    trackingEvents(app);
  }
}

module.exports = initialize;
<|endoftext|>