<commit_before>import Vue from 'vue'
Vue.config.productionTip = false

window.GobiertoPlans.PlanTypesController = (function() {

    function PlanTypesController() {}

    PlanTypesController.prototype.show = function() {
      if ($('body').attr('class').indexOf('gobierto_plans_plan_types_show') > -1) {
        _loadPlan();
      }
    };

    function _loadPlan() {

      // filters
      Vue.filter('translate', function (key) {
        if (!key) return
        return key[I18n.locale] || key["es"] || key["en"] || Object.values(key)[0] // fallback translations
      });

      Vue.filter('percent', function (value) {
        if (!value) return
        return (value / 100).toLocaleString(I18n.locale, { style: 'percent', maximumSignificantDigits: 4 })
      });

      Vue.filter('date', function (date) {
        if (!date) return
        return new Date(date).toLocaleString(I18n.locale, { year: 'numeric', month: 'short', day: 'numeric' })
      });

      // define the node root component
      Vue.component('node-root', {
        template: '#node-root-template',
        props: ['model'],
        data: function() {
          return {}
        },
        computed: {
          progressWidth: function () {
            return `${this.model.attributes.progress}%`
          }
        },
        methods: {
          open: function() {
            // Trigger event
            this.$emit('selection', { ...this.model });
          }
        }
      });

      // define the node list component
      Vue.component('node-list', {
        template: '#node-list-template',
        props: ['model', 'level'],
        data: function() {
          return {
            isOpen: false
          }
        },
        methods: {
          setActive: function() {
            if (this.model.type === "category" && !this.model.max_level) {
              var model = { ...this.model };

              this.$emit('selection', model);
            }

            if (this.model.type === "category" && this.model.max_level) {
              this.$emit("toggle");
              this.isOpen = !this.isOpen;
            }
          },
          getLabel: function(level, number_of_elements) {
            var key = this.level["level" + (level + 1)]
            return (number_of_elements == 1 ? key["one"] : key["other"])
          }
        }
      });

      // define the table view component
      Vue.component('table-view', {
        template: '#table-view-template',
        props: ['model', 'header', 'open'],
        data: function() {
          return {}
        },
        methods: {
          getProject: function(row) {
            if (this.open) {
              // var project = { ...this.model };
              var project = { ...row };

              this.$emit('selection', project);
            }
          }
        }
      });

      // main object
      new Vue({
        el: '#gobierto-planification',
        name: 'gobierto-planification',
        data: {
          json: {},
          levelKeys: {},
          optionKeys: [],
          activeNode: {},
          showTable: {},
          showTableHeader: true,
          openNode: false,
          globalProgress: 0,
          rootid: 0
        },
        created: function() {
          this.getJson();

          let vm = this
          function locationHashChanged() {
              vm.getPermalink(window.location.hash.substring(1))
          }

          window.onhashchange = locationHashChanged
        },
        watch: {
          activeNode: {
            handler: function(node) {
              this.showTable = {}
              // update hash when a new node is active
              this.setPermalink()

              this.isOpen(node.level)
            },
            deep: true
          }
        },
        computed: {
          availableOpts: function () {
            // Filter options object only to those values present in the configuration (optionKeys)
            return _.pick(this.activeNode.attributes.options, _.filter(_.keys(this.activeNode.attributes.options), function (o) {
              return _.includes(_.keys(this.optionKeys), o.toString().toLowerCase())
            }.bind(this)));
          }
        },
        methods: {
          getJson: function() {
            $.getJSON(window.location.href, {format: 'json'}, function(json) {
              // Tree with categories and the leaves (nodes)
              var data = json["plan_tree"];
              // Nodes can have variable attributes and these are their keys
              var optionKeys = json["option_keys"];
              // Keys for different levels
              var levelKeys = json["level_keys"];
              // If you can see the table header
              var showTableHeader = json["show_table_header"];
              // If you can open a node (project)
              var openNode = json["open_node"];
              // Global progress, provided by json
              var globalProgress = json["global_progress"];

              // Hide spinner
              $(".js-toggle-overlay").removeClass('is-active');

              this.json = data;
              this.levelKeys = levelKeys;
              this.showTableHeader = showTableHeader;
              this.openNode = openNode;
              this.globalProgress = globalProgress;
              this.optionKeys = Object.keys(optionKeys).reduce(function(c, k) {
                return (c[k.toLowerCase()] = optionKeys[k]), c;
              }, {});

              // Parse permalink
              if (window.location.hash) {
                this.getPermalink(window.location.hash.substring(1))
              }

            }.bind(this));
          },
          color: function() {
            return this.rootid % this.json.length + 1;
          },
          setRootColor: function(index) {
            return index % this.json.length + 1;
          },
          setSelection: function(model) {
            this.activeNode = model;

            // To know the root node
            this.rootid = this.activeNode.uid.toString().charAt(0);
          },
          isOpen: function(level) {
            if (this.activeNode.level === undefined) return false

            let isOpen = false
            if (this.activeNode.level === 0) {
              // activeNode = 0, it means is a "line"
              // then, it shows level_0 and level_1
              isOpen = (level < 2)
            } else {
              // activeNode = X
              if (this.activeNode.type === "node") {
                // type = node, it means there's no further levels, then it shows as previous one
                isOpen = (level === 0) || (level === this.activeNode.level)
              } else {
                // then, it shows level_0 and level_(X+1), but not those between
                isOpen = (level === 0) || (level === (this.activeNode.level + 1))
              }
            }

            return isOpen
          },
          typeOf: function(val) {
            if (_.isString(val)) {
              return "string"
            } else if (_.isArray(val)) {
              return "array"
            }
            return "object";
          },
          toggle: function(i) {
            Vue.set(this.showTable, i, !(this.showTable[i]));
          },
          getLabel: function(level, number_of_elements) {
            var l = (_.keys(this.levelKeys).length === (level + 1)) ? level : (level + 1);
            var key = this.levelKeys["level" + l];
            return (number_of_elements == 1 ? key["one"] : key["other"]);
          },
          getParent: function() {
            // Initialize args
            var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;

            // From uid, turno into array all parents, and drop last item (myself)
            var ancestors = _.dropRight(this.activeNode.uid.split('.')).map(Number);

            var current = this.json; // First item. ROOT item
            for (var i = 0; i < ancestors.length; i++) {
              if (i === breakpoint) {
                // If there is breakpoint, I get the corresponding ancestor set by breakpoint
                break;
              }

              if (!_.isArray(current)) {
                current = current.children;
              }
              current = current[ancestors[i]];
            }

            return current || {}
          },
          setParent: function() {
            // Initialize args
            var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
            //hack 3rd level (3rd level has no SECTION)
            if (breakpoint === 3) breakpoint = breakpoint - 1;

            this.activeNode = this.getParent(breakpoint);
          },
          setPermalink: function () {
            window.location.hash = this.activeNode.uid
          },
          getPermalink: function (hash) {
            let found = this.searchByUid(hash, this.json)
            if (found) {
              this.setSelection(found)
            }
          },
          searchByUid: function(id, data) {
            let result = false

            if (_.isArray(data)) {
              _.each(data, d => {
                result = findNodeByProp(id, d, 'uid')

                // Return false to break loop
                if (result !== false) {
                  return false;
                }
              })
            } else {
              result = findNodeByProp(id, data, 'uid')
            }

            return result
          }
        }
      })

      function findNodeByProp(id, currentNode, prop = 'id') {
        var i,
          currentChild,
          result;

        if (id == currentNode[prop]) {
          return currentNode;
        } else {

          // Use a for loop instead of forEach to avoid nested functions
          // Otherwise "return" will not work properly
          for (i = 0; i < currentNode.children.length; i += 1) {
            currentChild = currentNode.children[i];

            // Search in the current child
            result = findNodeByProp(id, currentChild, prop);

            // Return the result if the node has been found
            if (result !== false) {
              return result;
            }
          }

          // The node has not been found and we have no more options
          return false;
        }
      }
    }

    return PlanTypesController;
  })();

  window.GobiertoPlans.plan_types_controller = new GobiertoPlans.PlanTypesController;
<commit_msg>Fix percent method in component to use only one decimal<commit_after>import Vue from 'vue'
Vue.config.productionTip = false

window.GobiertoPlans.PlanTypesController = (function() {

    function PlanTypesController() {}

    PlanTypesController.prototype.show = function() {
      if ($('body').attr('class').indexOf('gobierto_plans_plan_types_show') > -1) {
        _loadPlan();
      }
    };

    function _loadPlan() {

      // filters
      Vue.filter('translate', function (key) {
        if (!key) return
        return key[I18n.locale] || key["es"] || key["en"] || Object.values(key)[0] // fallback translations
      });

      Vue.filter('percent', function (value) {
        if (!value) return
        return (value / 100).toLocaleString(I18n.locale, { style: 'percent', maximumFractionDigits: 1 })
      });

      Vue.filter('date', function (date) {
        if (!date) return
        return new Date(date).toLocaleString(I18n.locale, { year: 'numeric', month: 'short', day: 'numeric' })
      });

      // define the node root component
      Vue.component('node-root', {
        template: '#node-root-template',
        props: ['model'],
        data: function() {
          return {}
        },
        computed: {
          progressWidth: function () {
            return `${this.model.attributes.progress}%`
          }
        },
        methods: {
          open: function() {
            // Trigger event
            this.$emit('selection', { ...this.model });
          }
        }
      });

      // define the node list component
      Vue.component('node-list', {
        template: '#node-list-template',
        props: ['model', 'level'],
        data: function() {
          return {
            isOpen: false
          }
        },
        methods: {
          setActive: function() {
            if (this.model.type === "category" && !this.model.max_level) {
              var model = { ...this.model };

              this.$emit('selection', model);
            }

            if (this.model.type === "category" && this.model.max_level) {
              this.$emit("toggle");
              this.isOpen = !this.isOpen;
            }
          },
          getLabel: function(level, number_of_elements) {
            var key = this.level["level" + (level + 1)]
            return (number_of_elements == 1 ? key["one"] : key["other"])
          }
        }
      });

      // define the table view component
      Vue.component('table-view', {
        template: '#table-view-template',
        props: ['model', 'header', 'open'],
        data: function() {
          return {}
        },
        methods: {
          getProject: function(row) {
            if (this.open) {
              // var project = { ...this.model };
              var project = { ...row };

              this.$emit('selection', project);
            }
          }
        }
      });

      // main object
      new Vue({
        el: '#gobierto-planification',
        name: 'gobierto-planification',
        data: {
          json: {},
          levelKeys: {},
          optionKeys: [],
          activeNode: {},
          showTable: {},
          showTableHeader: true,
          openNode: false,
          globalProgress: 0,
          rootid: 0
        },
        created: function() {
          this.getJson();

          let vm = this
          function locationHashChanged() {
              vm.getPermalink(window.location.hash.substring(1))
          }

          window.onhashchange = locationHashChanged
        },
        watch: {
          activeNode: {
            handler: function(node) {
              this.showTable = {}
              // update hash when a new node is active
              this.setPermalink()

              this.isOpen(node.level)
            },
            deep: true
          }
        },
        computed: {
          availableOpts: function () {
            // Filter options object only to those values present in the configuration (optionKeys)
            return _.pick(this.activeNode.attributes.options, _.filter(_.keys(this.activeNode.attributes.options), function (o) {
              return _.includes(_.keys(this.optionKeys), o.toString().toLowerCase())
            }.bind(this)));
          }
        },
        methods: {
          getJson: function() {
            $.getJSON(window.location.href, {format: 'json'}, function(json) {
              // Tree with categories and the leaves (nodes)
              var data = json["plan_tree"];
              // Nodes can have variable attributes and these are their keys
              var optionKeys = json["option_keys"];
              // Keys for different levels
              var levelKeys = json["level_keys"];
              // If you can see the table header
              var showTableHeader = json["show_table_header"];
              // If you can open a node (project)
              var openNode = json["open_node"];
              // Global progress, provided by json
              var globalProgress = json["global_progress"];

              // Hide spinner
              $(".js-toggle-overlay").removeClass('is-active');

              this.json = data;
              this.levelKeys = levelKeys;
              this.showTableHeader = showTableHeader;
              this.openNode = openNode;
              this.globalProgress = globalProgress;
              this.optionKeys = Object.keys(optionKeys).reduce(function(c, k) {
                return (c[k.toLowerCase()] = optionKeys[k]), c;
              }, {});

              // Parse permalink
              if (window.location.hash) {
                this.getPermalink(window.location.hash.substring(1))
              }

            }.bind(this));
          },
          color: function() {
            return this.rootid % this.json.length + 1;
          },
          setRootColor: function(index) {
            return index % this.json.length + 1;
          },
          setSelection: function(model) {
            this.activeNode = model;

            // To know the root node
            this.rootid = this.activeNode.uid.toString().charAt(0);
          },
          isOpen: function(level) {
            if (this.activeNode.level === undefined) return false

            let isOpen = false
            if (this.activeNode.level === 0) {
              // activeNode = 0, it means is a "line"
              // then, it shows level_0 and level_1
              isOpen = (level < 2)
            } else {
              // activeNode = X
              if (this.activeNode.type === "node") {
                // type = node, it means there's no further levels, then it shows as previous one
                isOpen = (level === 0) || (level === this.activeNode.level)
              } else {
                // then, it shows level_0 and level_(X+1), but not those between
                isOpen = (level === 0) || (level === (this.activeNode.level + 1))
              }
            }

            return isOpen
          },
          typeOf: function(val) {
            if (_.isString(val)) {
              return "string"
            } else if (_.isArray(val)) {
              return "array"
            }
            return "object";
          },
          toggle: function(i) {
            Vue.set(this.showTable, i, !(this.showTable[i]));
          },
          getLabel: function(level, number_of_elements) {
            var l = (_.keys(this.levelKeys).length === (level + 1)) ? level : (level + 1);
            var key = this.levelKeys["level" + l];
            return (number_of_elements == 1 ? key["one"] : key["other"]);
          },
          getParent: function() {
            // Initialize args
            var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;

            // From uid, turno into array all parents, and drop last item (myself)
            var ancestors = _.dropRight(this.activeNode.uid.split('.')).map(Number);

            var current = this.json; // First item. ROOT item
            for (var i = 0; i < ancestors.length; i++) {
              if (i === breakpoint) {
                // If there is breakpoint, I get the corresponding ancestor set by breakpoint
                break;
              }

              if (!_.isArray(current)) {
                current = current.children;
              }
              current = current[ancestors[i]];
            }

            return current || {}
          },
          setParent: function() {
            // Initialize args
            var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
            //hack 3rd level (3rd level has no SECTION)
            if (breakpoint === 3) breakpoint = breakpoint - 1;

            this.activeNode = this.getParent(breakpoint);
          },
          setPermalink: function () {
            window.location.hash = this.activeNode.uid
          },
          getPermalink: function (hash) {
            let found = this.searchByUid(hash, this.json)
            if (found) {
              this.setSelection(found)
            }
          },
          searchByUid: function(id, data) {
            let result = false

            if (_.isArray(data)) {
              _.each(data, d => {
                result = findNodeByProp(id, d, 'uid')

                // Return false to break loop
                if (result !== false) {
                  return false;
                }
              })
            } else {
              result = findNodeByProp(id, data, 'uid')
            }

            return result
          }
        }
      })

      function findNodeByProp(id, currentNode, prop = 'id') {
        var i,
          currentChild,
          result;

        if (id == currentNode[prop]) {
          return currentNode;
        } else {

          // Use a for loop instead of forEach to avoid nested functions
          // Otherwise "return" will not work properly
          for (i = 0; i < currentNode.children.length; i += 1) {
            currentChild = currentNode.children[i];

            // Search in the current child
            result = findNodeByProp(id, currentChild, prop);

            // Return the result if the node has been found
            if (result !== false) {
              return result;
            }
          }

          // The node has not been found and we have no more options
          return false;
        }
      }
    }

    return PlanTypesController;
  })();

  window.GobiertoPlans.plan_types_controller = new GobiertoPlans.PlanTypesController;
<|endoftext|>


<commit_before>import React, { Component } from "react";
import PropTypes from "prop-types";

import GlobalNavAdapter from "../../../adapters/GlobalNav/GlobalNavAdapter";
import TopNavAdapter from "../../../adapters/GlobalNav/TopNav/TopNavAdapter";
import Search from "./TopNav/Search";
import SubNavAdapter from "../../../adapters/GlobalNav/SubNav/SubNavAdapter";
import HelpAdapter from "../../../adapters/GlobalNav/TopNav/HelpAdapter";
import GroupAdapter from "../../../adapters/GlobalNav/TopNav/GroupAdapter";
import OptionAdapter from "../../../adapters/GlobalNav/TopNav/OptionAdapter";
import SideNav from "./SideNav";
import Tabs from "./SubNav/Tabs";
import ProjectAccountSwitcher from "./TopNav/ProjectAccountSwitcher";
import Notifications from "./TopNav/Notifications";
import Notification from "./TopNav/Notification";

class GlobalNav extends Component {
  constructor(props) {
    super(props);
    this.state = {
      sideNavOpen: this.props.sideNavOpenByDefault || false
    };
  }

  handleHamburgerClick = event => {
    this.props.onHamburgerClick(event);
    this.setState({ sideNavOpen: !this.state.sideNavOpen });
  };

  showProjectAccountSwitcher() {
    const topNav = this.props.topNav || {};
    const projectAccountSwitcher = topNav.projectAccountSwitcher || {};
    const accounts = projectAccountSwitcher.accounts || [];
    const projects = projectAccountSwitcher.projects || [];
    return accounts.length > 0 || projects.length > 0;
  }

  showHelp() {
    return (
      this.props.topNav.help &&
      this.props.topNav.help.groups &&
      this.props.topNav.help.groups.length > 0
    );
  }

  showNotifications() {
    return true;
  }

  renderedSideNavOpen() {
    if (this.props.sideNavOpen !== undefined) {
      return this.props.sideNavOpen;
    }
    return this.state.sideNavOpen;
  }

  renderedActiveModule() {
    const activeSubmodule =
      this.props.submodules.find(s => s.id === this.props.activeModuleId) || {};
    const activeModule = this.props.modules.find(
      m =>
        m.id === activeSubmodule.moduleId || m.id === this.props.activeModuleId
    );

    if (activeModule === undefined) {
      return null;
    }

    activeModule.submodules = this.props.submodules.filter(
      s => s.moduleId === activeModule.id
    );
    return activeModule;
  }

  renderTab = submodule => (
    <Tabs.Tab id={submodule.id} label={submodule.title} key={submodule.id} />
  );

  /* eslint-disable react/no-array-index-key */
  render() {
    const activeModule = this.renderedActiveModule();

    return (

        <GlobalNavAdapter sideNavOpen={this.renderedSideNavOpen()}>
          <TopNavAdapter
            onHamburgerClick={this.handleHamburgerClick}
            {...this.props.topNav}
          >
            <Search
              options={this.props.topNav.searchOptions}
              onInput={this.props.topNav.onSearchInput}
              onSubmit={this.props.topNav.onSearchSubmit}
              value={this.props.topNav.searchValue}
            />
            {this.showProjectAccountSwitcher() ? (
              <ProjectAccountSwitcher
                {...this.props.topNav.projectAccountSwitcher}
              />
            ) : null}
            {this.showHelp() ? (
              <HelpAdapter {...this.props.topNav.help}>
                {(this.props.topNav.help.groups || []).map((groupProps, i) => (
                  <GroupAdapter key={i} {...groupProps}>
                    {(groupProps.options || []).map(optionProps => (
                      <OptionAdapter key={optionProps.name} {...optionProps} />
                    ))}
                  </GroupAdapter>
                ))}
              </HelpAdapter>
            ) : null}
            {this.showNotifications() ? (
              <Notifications {...this.props.topNav.notifications}>
                {(this.props.topNav.notifications.notifications || []
                ).map(notificationProps => (
                  <Notification
                    key={notificationProps.id}
                    {...notificationProps}
                  />
                ))}
              </Notifications>
            ) : null}
          </TopNavAdapter>
          <SideNav
            activeModuleId={this.props.activeModuleId}
            modules={this.props.modules}
            onModuleChange={this.props.onModuleChange}
            submodules={this.props.submodules}
            {...this.props.sideNav}
          />
          {activeModule && this.props.showSubNav ? (
            <SubNavAdapter
              moduleIndicatorName={activeModule.title}
              moduleIndicatorIcon={activeModule.icon}
            >
              {activeModule.submodules.length > 0 ? (
                <Tabs
                  selectedTabId={this.props.activeModuleId}
                  onChange={this.props.onModuleChange}
                >
                  {activeModule.submodules.map(this.renderTab)}
                </Tabs>
              ) : null}
            </SubNavAdapter>
          ) : null}
          {this.props.children ? this.props.children : null}
        </GlobalNavAdapter>

    );
  }
  /* eslint-enable react/no-array-index-key */
}

GlobalNav.propTypes = {
  activeModuleId: PropTypes.string,
  children: PropTypes.node,
  onHamburgerClick: PropTypes.func,
  onModuleChange: PropTypes.func.isRequired,
  showSubNav: PropTypes.bool,
  sideNavOpen: PropTypes.bool,
  sideNavOpenByDefault: PropTypes.bool,
  modules: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired
    })
  ),
  submodules: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired
    })
  ),
  topNav: PropTypes.shape({
    logo: PropTypes.string,
    logoLink: PropTypes.string,
    projectAccountSwitcher: PropTypes.shape({
      accounts: PropTypes.array,
      projects: PropTypes.array,
      projectTitle: PropTypes.string,
      accountTitle: PropTypes.string,
      onProjectClick: PropTypes.func,
      onAccountClick: PropTypes.func,
      activeProjectId: PropTypes.string,
      activeAccountId: PropTypes.string
    }),
    help: PropTypes.shape({
      groups: PropTypes.arrayOf(
        PropTypes.shape({
          name: PropTypes.string
        })
      )
    })
  }),
  sideNav: PropTypes.shape({
    copyright: PropTypes.string,
    headerLabel: PropTypes.string,
    headerLink: PropTypes.string,
    links: PropTypes.arrayOf(PropTypes.object),
    onHeaderClick: PropTypes.func,
    onSuperHeaderClick: PropTypes.func,
    superHeaderLabel: PropTypes.string,
    superHeaderLink: PropTypes.string
  })
};

GlobalNav.defaultProps = {
  activeModuleId: undefined,
  children: undefined,
  modules: [],
  submodules: [],
  topNav: {},
  sideNav: {},
  onHamburgerClick: () => {},
  showSubNav: false,
  sideNavOpen: undefined,
  sideNavOpenByDefault: false
};

GlobalNav.__docgenInfo = {
  props: {
    activeModuleId: {
      description: "id of the active module or submodule"
    },
    children: {
      description: "page content"
    },
    onHamburgerClick: {
      description:
        "called when the user clicks on the 'hamburger' button in order to toggle the menu"
    },
    onModuleChange: {
      description: "called when the user selects a module or submodule"
    },
    showSubNav: {
      description: "when true, shows the Subnav below the Topnav"
    },
    sideNavOpen: {
      description: "when true, Sidenav is open"
    },
    sideNavOpenByDefault: {
      description:
        "initial open state of the side nav, user actions will override"
    },
    modules: {
      description: "a list of modules to appear in the sidenav"
    },
    submodules: {
      description: "a list of submodules to appear in the sidenav"
    },
    topNav: {
      description: "options to configure the Topnav"
    },
    sideNav: {
      description: "options to configure the Sidenav"
    }
  }
};

export default GlobalNav;
<commit_msg>Fix Globablnav spec<commit_after>import React, { Component } from "react";
import PropTypes from "prop-types";

import GlobalNavAdapter from "../../../adapters/GlobalNav/GlobalNavAdapter";
import TopNavAdapter from "../../../adapters/GlobalNav/TopNav/TopNavAdapter";
import Search from "./TopNav/Search";
import SubNavAdapter from "../../../adapters/GlobalNav/SubNav/SubNavAdapter";
import HelpAdapter from "../../../adapters/GlobalNav/TopNav/HelpAdapter";
import GroupAdapter from "../../../adapters/GlobalNav/TopNav/GroupAdapter";
import OptionAdapter from "../../../adapters/GlobalNav/TopNav/OptionAdapter";
import SideNav from "./SideNav";
import Tabs from "./SubNav/Tabs";
import ProjectAccountSwitcher from "./TopNav/ProjectAccountSwitcher";
import Notifications from "./TopNav/Notifications";
import Notification from "./TopNav/Notification";

class GlobalNav extends Component {
  constructor(props) {
    super(props);
    this.state = {
      sideNavOpen: this.props.sideNavOpenByDefault || false
    };
  }

  handleHamburgerClick = event => {
    this.props.onHamburgerClick(event);
    this.setState({ sideNavOpen: !this.state.sideNavOpen });
  };

  showProjectAccountSwitcher() {
    const topNav = this.props.topNav || {};
    const projectAccountSwitcher = topNav.projectAccountSwitcher || {};
    const accounts = projectAccountSwitcher.accounts || [];
    const projects = projectAccountSwitcher.projects || [];
    return accounts.length > 0 || projects.length > 0;
  }

  showHelp() {
    return (
      this.props.topNav.help &&
      this.props.topNav.help.groups &&
      this.props.topNav.help.groups.length > 0
    );
  }

  showNotifications() {
    return (
      this.props.topNav.notifications &&
      this.props.topNav.notifications.notifications
    );
  }

  renderedSideNavOpen() {
    if (this.props.sideNavOpen !== undefined) {
      return this.props.sideNavOpen;
    }
    return this.state.sideNavOpen;
  }

  renderedActiveModule() {
    const activeSubmodule =
      this.props.submodules.find(s => s.id === this.props.activeModuleId) || {};
    const activeModule = this.props.modules.find(
      m =>
        m.id === activeSubmodule.moduleId || m.id === this.props.activeModuleId
    );

    if (activeModule === undefined) {
      return null;
    }

    activeModule.submodules = this.props.submodules.filter(
      s => s.moduleId === activeModule.id
    );
    return activeModule;
  }

  renderTab = submodule => (
    <Tabs.Tab id={submodule.id} label={submodule.title} key={submodule.id} />
  );

  /* eslint-disable react/no-array-index-key */
  render() {
    const activeModule = this.renderedActiveModule();

    return (

        <GlobalNavAdapter sideNavOpen={this.renderedSideNavOpen()}>
          <TopNavAdapter
            onHamburgerClick={this.handleHamburgerClick}
            {...this.props.topNav}
          >
            <Search
              options={this.props.topNav.searchOptions}
              onInput={this.props.topNav.onSearchInput}
              onSubmit={this.props.topNav.onSearchSubmit}
              value={this.props.topNav.searchValue}
            />
            {this.showProjectAccountSwitcher() ? (
              <ProjectAccountSwitcher
                {...this.props.topNav.projectAccountSwitcher}
              />
            ) : null}
            {this.showHelp() ? (
              <HelpAdapter {...this.props.topNav.help}>
                {(this.props.topNav.help.groups || []).map((groupProps, i) => (
                  <GroupAdapter key={i} {...groupProps}>
                    {(groupProps.options || []).map(optionProps => (
                      <OptionAdapter key={optionProps.name} {...optionProps} />
                    ))}
                  </GroupAdapter>
                ))}
              </HelpAdapter>
            ) : null}
            {this.showNotifications() ? (
              <Notifications {...this.props.topNav.notifications}>
                {(this.props.topNav.notifications.notifications || []
                ).map(notificationProps => (
                  <Notification
                    key={notificationProps.id}
                    {...notificationProps}
                  />
                ))}
              </Notifications>
            ) : null}
          </TopNavAdapter>
          <SideNav
            activeModuleId={this.props.activeModuleId}
            modules={this.props.modules}
            onModuleChange={this.props.onModuleChange}
            submodules={this.props.submodules}
            {...this.props.sideNav}
          />
          {activeModule && this.props.showSubNav ? (
            <SubNavAdapter
              moduleIndicatorName={activeModule.title}
              moduleIndicatorIcon={activeModule.icon}
            >
              {activeModule.submodules.length > 0 ? (
                <Tabs
                  selectedTabId={this.props.activeModuleId}
                  onChange={this.props.onModuleChange}
                >
                  {activeModule.submodules.map(this.renderTab)}
                </Tabs>
              ) : null}
            </SubNavAdapter>
          ) : null}
          {this.props.children ? this.props.children : null}
        </GlobalNavAdapter>

    );
  }
  /* eslint-enable react/no-array-index-key */
}

GlobalNav.propTypes = {
  activeModuleId: PropTypes.string,
  children: PropTypes.node,
  onHamburgerClick: PropTypes.func,
  onModuleChange: PropTypes.func.isRequired,
  showSubNav: PropTypes.bool,
  sideNavOpen: PropTypes.bool,
  sideNavOpenByDefault: PropTypes.bool,
  modules: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired
    })
  ),
  submodules: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired
    })
  ),
  topNav: PropTypes.shape({
    logo: PropTypes.string,
    logoLink: PropTypes.string,
    projectAccountSwitcher: PropTypes.shape({
      accounts: PropTypes.array,
      projects: PropTypes.array,
      projectTitle: PropTypes.string,
      accountTitle: PropTypes.string,
      onProjectClick: PropTypes.func,
      onAccountClick: PropTypes.func,
      activeProjectId: PropTypes.string,
      activeAccountId: PropTypes.string
    }),
    help: PropTypes.shape({
      groups: PropTypes.arrayOf(
        PropTypes.shape({
          name: PropTypes.string
        })
      )
    })
  }),
  sideNav: PropTypes.shape({
    copyright: PropTypes.string,
    headerLabel: PropTypes.string,
    headerLink: PropTypes.string,
    links: PropTypes.arrayOf(PropTypes.object),
    onHeaderClick: PropTypes.func,
    onSuperHeaderClick: PropTypes.func,
    superHeaderLabel: PropTypes.string,
    superHeaderLink: PropTypes.string
  })
};

GlobalNav.defaultProps = {
  activeModuleId: undefined,
  children: undefined,
  modules: [],
  submodules: [],
  topNav: {},
  sideNav: {},
  onHamburgerClick: () => {},
  showSubNav: false,
  sideNavOpen: undefined,
  sideNavOpenByDefault: false
};

GlobalNav.__docgenInfo = {
  props: {
    activeModuleId: {
      description: "id of the active module or submodule"
    },
    children: {
      description: "page content"
    },
    onHamburgerClick: {
      description:
        "called when the user clicks on the 'hamburger' button in order to toggle the menu"
    },
    onModuleChange: {
      description: "called when the user selects a module or submodule"
    },
    showSubNav: {
      description: "when true, shows the Subnav below the Topnav"
    },
    sideNavOpen: {
      description: "when true, Sidenav is open"
    },
    sideNavOpenByDefault: {
      description:
        "initial open state of the side nav, user actions will override"
    },
    modules: {
      description: "a list of modules to appear in the sidenav"
    },
    submodules: {
      description: "a list of submodules to appear in the sidenav"
    },
    topNav: {
      description: "options to configure the Topnav"
    },
    sideNav: {
      description: "options to configure the Sidenav"
    }
  }
};

export default GlobalNav;
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { Link } from 'react-router'
import { logout } from '../actions'
import { connect } from 'react-redux'

class NavbarComponent extends Component {

  handleLogout(e) {
    e.preventDefault()
    const { dispatch } = this.props
    dispatch(logout())
  }

  render() {
    return (

            <button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-2">
              Toggle navigation

            </button>
            <Link to='/app' className="navbar-brand logo-font">Blurblarp</Link>

                { this.props.user.email }

                Logout

    );
  }
}

export const Navbar = connect(state => state.auth)(NavbarComponent)
<commit_msg>Update Navbar.js<commit_after>import React, { Component } from 'react'
import { Link } from 'react-router'
import { logout } from '../actions'
import { connect } from 'react-redux'

class NavbarComponent extends Component {

  handleLogout(e) {
    e.preventDefault()
    const { dispatch } = this.props
    dispatch(logout())
  }

  render() {
    return (

            <button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-2">
              Toggle navigation

            </button>
            <Link to='/app' className="navbar-brand logo-font">Thxbro!</Link>

                { this.props.user.email }

                Logout

    );
  }
}

export const Navbar = connect(state => state.auth)(NavbarComponent)
<|endoftext|>


<commit_before>Template.usersSettingsTableActions.helpers({
  "userCanDeleteAccount" () {
    // Prevent user from deleting own account
    // by hiding delete button

    // get logged in user ID
    const loggedInUserId = Meteor.userId();

    // get users table user id
    const tableUserId = this._id;

    if (loggedInUserId !== tableUserId) {
      // logged in user different from table user
      // so, allow delete
      return true;
    }

    // otherwise, don't allow delete (hide button)
    return false;
  },
});

Template.usersSettingsTableActions.events({
  'click .delete-user' (event, template) {
    // Save reference to user
    var user = this;

    // Show edit modal, passing in user as data context
    Modal.show("deleteUser", {"data": user}, {});
  },
  'click .edit-user' (event, template) {
    // Save reference to user
    var user = this;

    // Show edit modal, passing in user as data context
    Modal.show("editUser", {"data": user}, {});
  },
  'click .edit-user-groups' (event, template) {
    // Save reference to user
    const user = this;

    // Show edit modal, passing in user as data context
    Modal.show("editUserGroups", {"data": user}, {});
  }
});
<commit_msg>Lint, pass in user object as 'user'<commit_after>Template.usersSettingsTableActions.helpers({
  userCanDeleteAccount() {
    // Prevent user from deleting own account
    // by hiding delete button

    // get logged in user ID
    const loggedInUserId = Meteor.userId();

    // get users table user id
    const tableUserId = this._id;

    if (loggedInUserId !== tableUserId) {
      // logged in user different from table user
      // so, allow delete
      return true;
    }

    // otherwise, don't allow delete (hide button)
    return false;
  }
});

Template.usersSettingsTableActions.events({
  "click .delete-user"(event, template) {
    // Save reference to user
    var user = this;

    // Show edit modal, passing in user as data context
    Modal.show("deleteUser", { data: user }, {});
  },
  "click .edit-user"(event, template) {
    // Save reference to user
    var user = this;

    // Show edit modal, passing in user as data context
    Modal.show("editUser", { data: user }, {});
  },
  "click .edit-user-groups"(event, template) {
    // Save reference to user
    const user = this;

    // Show edit modal, passing in user as data context
    Modal.show("editUserGroups", { user }, {});
  }
});
<|endoftext|>


<commit_before>/*
  File: Core.js

  Description:

  Provides common utility functions and the Class object used internally by the library.

  Also provides the <TreeUtil> object for manipulating JSON tree structures

  Some of the Basic utility functions and the Class system are based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.

  Author:

  Nicolas Garcia Belmonte

  Copyright:

  Copyright 2008-2009 by Nicolas Garcia Belmonte.

  Homepage:

  <http://thejit.org>

  Version:

  1.1.3

  License:

  BSD License
 */

window.$jit = function(w) {
  w = w || window;
  for(var k in $jit) {
    if($jit[k].$extend) {
      w[k] = $jit[k];
    }
  }
};

//TODO(nico) add these methods to $jit.util
var $ = function(d) {
  return document.getElementById(d);
};

$.empty = function() {
};

$.extend = function(original, extended) {
  for ( var key in (extended || {}))
    original[key] = extended[key];
  return original;
};

$.lambda = function(value) {
  return (typeof value == 'function') ? value : function() {
    return value;
  };
};

$.time = Date.now || function() {
  return +new Date;
};

$.splat = function(obj) {
  var type = $.type(obj);
  return type ? ((type != 'array') ? [ obj ] : obj) : [];
};

$.type = function(elem) {
  return $.type.s.call(elem).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
};
$.type.s = Object.prototype.toString;

$.each = function(iterable, fn) {
  var type = $.type(iterable);
  if (type == 'object') {
    for ( var key in iterable)
      fn(iterable[key], key);
  } else {
    for ( var i = 0; i < iterable.length; i++)
      fn(iterable[i], i);
  }
};

$.map = function(array, f) {
  var ans = [];
  $.each(array, function(elem, i) {
    ans.push(f(elem, i));
  });
  return ans;
};

$.reduce = function(array, f, opt) {
  var l = array.length;
  if(l==0) return opt;
  var acum = opt || array[--l];
  while(l--) {
    acum = f(array[l], acum);
  }
  return acum;
};

$.merge = function() {
  var mix = {};
  for ( var i = 0, l = arguments.length; i < l; i++) {
    var object = arguments[i];
    if ($.type(object) != 'object')
      continue;
    for ( var key in object) {
      var op = object[key], mp = mix[key];
      mix[key] = (mp && $.type(op) == 'object' && $.type(mp) == 'object') ? $
          .merge(mp, op) : $.unlink(op);
    }
  }
  return mix;
};

$.unlink = function(object) {
  var unlinked;
  switch ($.type(object)) {
  case 'object':
    unlinked = {};
    for ( var p in object)
      unlinked[p] = $.unlink(object[p]);
    break;
  case 'array':
    unlinked = [];
    for ( var i = 0, l = object.length; i < l; i++)
      unlinked[i] = $.unlink(object[i]);
    break;
  default:
    return object;
  }
  return unlinked;
};

$.zip = function() {
  if(arguments.length === 0) return [];
  for(var j=0, ans=[], l=arguments.length, ml=arguments[0].length; j<ml; j++) {
    for(var i=0, row=[]; i<l; i++) {
      row.push(arguments[i][j]);
    }
    ans.push(row);
  }
  return ans;
};

$.rgbToHex = function(srcArray, array) {
  if (srcArray.length < 3)
    return null;
  if (srcArray.length == 4 && srcArray[3] == 0 && !array)
    return 'transparent';
  var hex = [];
  for ( var i = 0; i < 3; i++) {
    var bit = (srcArray[i] - 0).toString(16);
    hex.push(bit.length == 1 ? '0' + bit : bit);
  }
  return array ? hex : '#' + hex.join('');
};

$.hexToRgb = function(hex) {
  if (hex.length != 7) {
    hex = hex.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
    hex.shift();
    if (hex.length != 3)
      return null;
    var rgb = [];
    for ( var i = 0; i < 3; i++) {
      var value = hex[i];
      if (value.length == 1)
        value += value;
      rgb.push(parseInt(value, 16));
    }
    return rgb;
  } else {
    hex = parseInt(hex.slice(1), 16);
    return [ hex >> 16, hex >> 8 & 0xff, hex & 0xff ];
  }
};

$.destroy = function(elem) {
  $.clean(elem);
  if (elem.parentNode)
    elem.parentNode.removeChild(elem);
  if (elem.clearAttributes)
    elem.clearAttributes();
};

$.clean = function(elem) {
  for ( var ch = elem.childNodes, i = 0, l = ch.length; i < l; i++) {
    $.destroy(ch[i]);
  }
};

$.addEvent = function(obj, type, fn) {
  if (obj.addEventListener)
    obj.addEventListener(type, fn, false);
  else
    obj.attachEvent('on' + type, fn);
};

$.hasClass = function(obj, klass) {
  return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1;
};

$.addClass = function(obj, klass) {
  if (!$.hasClass(obj, klass))
    obj.className = (obj.className + " " + klass);
};

$.removeClass = function(obj, klass) {
  obj.className = obj.className.replace(new RegExp(
      '(^|\\s)' + klass + '(?:\\s|$)'), '$1');
};

$.getPos = function(elem) {
  if (elem.getBoundingClientRect) {
    var bound = elem.getBoundingClientRect(), html = elem.ownerDocument.documentElement;
    return {
      x: bound.left + html.scrollLeft - html.clientLeft,
      y: bound.top + html.scrollTop - html.clientTop
    };
  }

  var offset = getOffsets(elem);
  var scroll = getScrolls(elem);

  return {
    x: offset.x - scroll.x,
    y: offset.y - scroll.y
  };

  function getOffsets(elem) {
    var position = {
      x: 0,
      y: 0
    };
    while (elem && !isBody(elem)) {
      position.x += elem.offsetLeft;
      position.y += elem.offsetTop;
      elem = elem.offsetParent;
    }
    return position;
  }

  function getScrolls(elem) {
    var position = {
      x: 0,
      y: 0
    };
    while (elem && !isBody(elem)) {
      position.x += elem.scrollLeft;
      position.y += elem.scrollTop;
      elem = elem.parentNode;
    }
    return position;
  }

  function isBody(element) {
    return (/^(?:body|html)$/i).test(element.tagName);
  }
};

var Class = function(properties) {
  properties = properties || {};
  var klass = function() {
    for ( var key in this) {
      if (typeof this[key] != 'function')
        this[key] = $.unlink(this[key]);
    }
    this.constructor = klass;
    if (Class.prototyping)
      return this;
    var instance = this.initialize ? this.initialize.apply(this, arguments)
        : this;
    return instance;
  };

  for ( var mutator in Class.Mutators) {
    if (!properties[mutator])
      continue;
    properties = Class.Mutators[mutator](properties, properties[mutator]);
    delete properties[mutator];
  }

  $.extend(klass, this);
  klass.constructor = Class;
  klass.prototype = properties;
  return klass;
};

Class.Mutators = {

  Implements: function(self, klasses) {
    $.each($.splat(klasses), function(klass) {
      Class.prototyping = klass;
      var instance = (typeof klass == 'function') ? new klass : klass;
      for ( var prop in instance) {
        if (!(prop in self)) {
          self[prop] = instance[prop];
        }
      }
      delete Class.prototyping;
    });
    return self;
  }

};

$.extend(Class, {

  inherit: function(object, properties) {
    for ( var key in properties) {
      var override = properties[key];
      var previous = object[key];
      var type = $.type(override);
      if (previous && type == 'function') {
        if (override != previous) {
          Class.override(object, key, override);
        }
      } else if (type == 'object') {
        object[key] = $.merge(previous, override);
      } else {
        object[key] = override;
      }
    }
    return object;
  },

  override: function(object, name, method) {
    var parent = Class.prototyping;
    if (parent && object[name] != parent[name])
      parent = null;
    var override = function() {
      var previous = this.parent;
      this.parent = parent ? parent[name] : object[name];
      var value = method.apply(this, arguments);
      this.parent = previous;
      return value;
    };
    object[name] = override;
  }

});

Class.prototype.implement = function() {
  var proto = this.prototype;
  $.each(Array.prototype.slice.call(arguments || []), function(properties) {
    Class.inherit(proto, properties);
  });
  return this;
};

var Event = {
  getPos: function(e, win) {
    // get mouse position
    win = win || window;
    e = e || win.event;
    var doc = win.document;
    doc = doc.html || doc.body;
    var page = {
      x: e.pageX || e.clientX + doc.scrollLeft,
      y: e.pageY || e.clientY + doc.scrollTop
    };
    return page;
  }
};

//common json manipulation methods
$jit.json = {

  /*
     Method: prune

     Clears all tree nodes having depth greater than maxLevel.

     Parameters:

        tree - A JSON tree object. For more information please see <Loader.loadJSON>.
        maxLevel - An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.

  */
  prune: function(tree, maxLevel) {
    this.each(tree, function(elem, i) {
      if (i == maxLevel && elem.children) {
        delete elem.children;
        elem.children = [];
      }
    });
  },

  /*
     Method: getParent

     Returns the parent node of the node having _id_ as id.

     Parameters:

        tree - A JSON tree object. See also <Loader.loadJSON>.
        id - The _id_ of the child node whose parent will be returned.

    Returns:

        A tree JSON node if any, or false otherwise.

  */
  getParent: function(tree, id) {
    if (tree.id == id)
      return false;
    var ch = tree.children;
    if (ch && ch.length > 0) {
      for ( var i = 0; i < ch.length; i++) {
        if (ch[i].id == id)
          return tree;
        else {
          var ans = this.getParent(ch[i], id);
          if (ans)
            return ans;
        }
      }
    }
    return false;
  },

  /*
     Method: getSubtree

     Returns the subtree that matches the given id.

     Parameters:

        tree - A JSON tree object. See also <Loader.loadJSON>.
        id - A node *unique* identifier.

     Returns:

        A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.

  */
  getSubtree: function(tree, id) {
    if (tree.id == id)
      return tree;
    for ( var i = 0, ch = tree.children; i < ch.length; i++) {
      var t = this.getSubtree(ch[i], id);
      if (t != null)
        return t;
    }
    return null;
  },

  /*
     Method: getLeaves

      Returns the leaves of the tree.

     Parameters:

        node - A JSON tree node. See also <Loader.loadJSON>.
        maxLevel - _optional_ A subtree's max level.

     Returns:

     An array having objects with two properties.

      - The _node_ property contains the leaf node.
      - The _level_ property specifies the depth of the node.

  */
  getLeaves: function(node, maxLevel) {
    var leaves = [], levelsToShow = maxLevel || Number.MAX_VALUE;
    this.each(node, function(elem, i) {
      if (i < levelsToShow && (!elem.children || elem.children.length == 0)) {
        leaves.push( {
          'node': elem,
          'level': levelsToShow - i
        });
      }
    });
    return leaves;
  },

  /*
     Method: eachLevel

      Iterates on tree nodes with relative depth less or equal than a specified level.

     Parameters:

        tree - A JSON tree or subtree. See also <Loader.loadJSON>.
        initLevel - An integer specifying the initial relative level. Usually zero.
        toLevel - An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.
        action - A function that receives a node and an integer specifying the actual level of the node.

    Example:
   (start code js)
     TreeUtil.eachLevel(tree, 0, 3, function(node, depth) {
        alert(node.name + ' ' + depth);
     });
   (end code)
  */
  eachLevel: function(tree, initLevel, toLevel, action) {
    if (initLevel <= toLevel) {
      action(tree, initLevel);
      for ( var i = 0, ch = tree.children; i < ch.length; i++) {
        this.eachLevel(ch[i], initLevel + 1, toLevel, action);
      }
    }
  },

  /*
     Method: each

      A tree iterator.

     Parameters:

        tree - A JSON tree or subtree. See also <Loader.loadJSON>.
        action - A function that receives a node.

    Example:
    (start code js)
      TreeUtil.each(tree, function(node) {
        alert(node.name);
      });
    (end code)

  */
  each: function(tree, action) {
    this.eachLevel(tree, 0, Number.MAX_VALUE, action);
  },

  /*
     Method: loadSubtrees

      Appends subtrees to leaves by requesting new subtrees
      with the _request_ method.

     Parameters:

        tree - A JSON tree node. <Loader.loadJSON>.
        controller - An object that implements a request method.

     Example:
      (start code js)
        TreeUtil.loadSubtrees(leafNode, {
          request: function(nodeId, level, onComplete) {
            //Pseudo-code to make an ajax request for a new subtree
            // that has as root id _nodeId_ and depth _level_ ...
            Ajax.request({
              'url': 'http://subtreerequesturl/',

              onSuccess: function(json) {
                onComplete.onComplete(nodeId, json);
              }
            });
          }
        });
      (end code)
  */
  loadSubtrees: function(tree, controller) {
    var maxLevel = controller.request && controller.levelsToShow;
    var leaves = this.getLeaves(tree, maxLevel), len = leaves.length, selectedNode = {};
    if (len == 0)
      controller.onComplete();
    for ( var i = 0, counter = 0; i < len; i++) {
      var leaf = leaves[i], id = leaf.node.id;
      selectedNode[id] = leaf.node;
      controller.request(id, leaf.level, {
        onComplete: function(nodeId, tree) {
          var ch = tree.children;
          selectedNode[nodeId].children = ch;
          if (++counter == len) {
            controller.onComplete();
          }
        }
      });
    }
  }
};
<commit_msg>Small tweaks to the reduce method<commit_after>/*
  File: Core.js

  Description:

  Provides common utility functions and the Class object used internally by the library.

  Also provides the <TreeUtil> object for manipulating JSON tree structures

  Some of the Basic utility functions and the Class system are based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.

  Author:

  Nicolas Garcia Belmonte

  Copyright:

  Copyright 2008-2009 by Nicolas Garcia Belmonte.

  Homepage:

  <http://thejit.org>

  Version:

  1.1.3

  License:

  BSD License
 */

window.$jit = function(w) {
  w = w || window;
  for(var k in $jit) {
    if($jit[k].$extend) {
      w[k] = $jit[k];
    }
  }
};

//TODO(nico) add these methods to $jit.util
var $ = function(d) {
  return document.getElementById(d);
};

$.empty = function() {
};

$.extend = function(original, extended) {
  for ( var key in (extended || {}))
    original[key] = extended[key];
  return original;
};

$.lambda = function(value) {
  return (typeof value == 'function') ? value : function() {
    return value;
  };
};

$.time = Date.now || function() {
  return +new Date;
};

$.splat = function(obj) {
  var type = $.type(obj);
  return type ? ((type != 'array') ? [ obj ] : obj) : [];
};

$.type = function(elem) {
  return $.type.s.call(elem).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
};
$.type.s = Object.prototype.toString;

$.each = function(iterable, fn) {
  var type = $.type(iterable);
  if (type == 'object') {
    for ( var key in iterable)
      fn(iterable[key], key);
  } else {
    for ( var i = 0; i < iterable.length; i++)
      fn(iterable[i], i);
  }
};

$.map = function(array, f) {
  var ans = [];
  $.each(array, function(elem, i) {
    ans.push(f(elem, i));
  });
  return ans;
};

$.reduce = function(array, f, opt) {
  var l = array.length;
  if(l==0) return opt;
  var acum = arguments.length == 3? opt : array[--l];
  while(l--) {
    acum = f(acum, array[l]);
  }
  return acum;
};

$.merge = function() {
  var mix = {};
  for ( var i = 0, l = arguments.length; i < l; i++) {
    var object = arguments[i];
    if ($.type(object) != 'object')
      continue;
    for ( var key in object) {
      var op = object[key], mp = mix[key];
      mix[key] = (mp && $.type(op) == 'object' && $.type(mp) == 'object') ? $
          .merge(mp, op) : $.unlink(op);
    }
  }
  return mix;
};

$.unlink = function(object) {
  var unlinked;
  switch ($.type(object)) {
  case 'object':
    unlinked = {};
    for ( var p in object)
      unlinked[p] = $.unlink(object[p]);
    break;
  case 'array':
    unlinked = [];
    for ( var i = 0, l = object.length; i < l; i++)
      unlinked[i] = $.unlink(object[i]);
    break;
  default:
    return object;
  }
  return unlinked;
};

$.zip = function() {
  if(arguments.length === 0) return [];
  for(var j=0, ans=[], l=arguments.length, ml=arguments[0].length; j<ml; j++) {
    for(var i=0, row=[]; i<l; i++) {
      row.push(arguments[i][j]);
    }
    ans.push(row);
  }
  return ans;
};

$.rgbToHex = function(srcArray, array) {
  if (srcArray.length < 3)
    return null;
  if (srcArray.length == 4 && srcArray[3] == 0 && !array)
    return 'transparent';
  var hex = [];
  for ( var i = 0; i < 3; i++) {
    var bit = (srcArray[i] - 0).toString(16);
    hex.push(bit.length == 1 ? '0' + bit : bit);
  }
  return array ? hex : '#' + hex.join('');
};

$.hexToRgb = function(hex) {
  if (hex.length != 7) {
    hex = hex.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
    hex.shift();
    if (hex.length != 3)
      return null;
    var rgb = [];
    for ( var i = 0; i < 3; i++) {
      var value = hex[i];
      if (value.length == 1)
        value += value;
      rgb.push(parseInt(value, 16));
    }
    return rgb;
  } else {
    hex = parseInt(hex.slice(1), 16);
    return [ hex >> 16, hex >> 8 & 0xff, hex & 0xff ];
  }
};

$.destroy = function(elem) {
  $.clean(elem);
  if (elem.parentNode)
    elem.parentNode.removeChild(elem);
  if (elem.clearAttributes)
    elem.clearAttributes();
};

$.clean = function(elem) {
  for ( var ch = elem.childNodes, i = 0, l = ch.length; i < l; i++) {
    $.destroy(ch[i]);
  }
};

$.addEvent = function(obj, type, fn) {
  if (obj.addEventListener)
    obj.addEventListener(type, fn, false);
  else
    obj.attachEvent('on' + type, fn);
};

$.hasClass = function(obj, klass) {
  return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1;
};

$.addClass = function(obj, klass) {
  if (!$.hasClass(obj, klass))
    obj.className = (obj.className + " " + klass);
};

$.removeClass = function(obj, klass) {
  obj.className = obj.className.replace(new RegExp(
      '(^|\\s)' + klass + '(?:\\s|$)'), '$1');
};

$.getPos = function(elem) {
  if (elem.getBoundingClientRect) {
    var bound = elem.getBoundingClientRect(), html = elem.ownerDocument.documentElement;
    return {
      x: bound.left + html.scrollLeft - html.clientLeft,
      y: bound.top + html.scrollTop - html.clientTop
    };
  }

  var offset = getOffsets(elem);
  var scroll = getScrolls(elem);

  return {
    x: offset.x - scroll.x,
    y: offset.y - scroll.y
  };

  function getOffsets(elem) {
    var position = {
      x: 0,
      y: 0
    };
    while (elem && !isBody(elem)) {
      position.x += elem.offsetLeft;
      position.y += elem.offsetTop;
      elem = elem.offsetParent;
    }
    return position;
  }

  function getScrolls(elem) {
    var position = {
      x: 0,
      y: 0
    };
    while (elem && !isBody(elem)) {
      position.x += elem.scrollLeft;
      position.y += elem.scrollTop;
      elem = elem.parentNode;
    }
    return position;
  }

  function isBody(element) {
    return (/^(?:body|html)$/i).test(element.tagName);
  }
};

var Class = function(properties) {
  properties = properties || {};
  var klass = function() {
    for ( var key in this) {
      if (typeof this[key] != 'function')
        this[key] = $.unlink(this[key]);
    }
    this.constructor = klass;
    if (Class.prototyping)
      return this;
    var instance = this.initialize ? this.initialize.apply(this, arguments)
        : this;
    return instance;
  };

  for ( var mutator in Class.Mutators) {
    if (!properties[mutator])
      continue;
    properties = Class.Mutators[mutator](properties, properties[mutator]);
    delete properties[mutator];
  }

  $.extend(klass, this);
  klass.constructor = Class;
  klass.prototype = properties;
  return klass;
};

Class.Mutators = {

  Implements: function(self, klasses) {
    $.each($.splat(klasses), function(klass) {
      Class.prototyping = klass;
      var instance = (typeof klass == 'function') ? new klass : klass;
      for ( var prop in instance) {
        if (!(prop in self)) {
          self[prop] = instance[prop];
        }
      }
      delete Class.prototyping;
    });
    return self;
  }

};

$.extend(Class, {

  inherit: function(object, properties) {
    for ( var key in properties) {
      var override = properties[key];
      var previous = object[key];
      var type = $.type(override);
      if (previous && type == 'function') {
        if (override != previous) {
          Class.override(object, key, override);
        }
      } else if (type == 'object') {
        object[key] = $.merge(previous, override);
      } else {
        object[key] = override;
      }
    }
    return object;
  },

  override: function(object, name, method) {
    var parent = Class.prototyping;
    if (parent && object[name] != parent[name])
      parent = null;
    var override = function() {
      var previous = this.parent;
      this.parent = parent ? parent[name] : object[name];
      var value = method.apply(this, arguments);
      this.parent = previous;
      return value;
    };
    object[name] = override;
  }

});

Class.prototype.implement = function() {
  var proto = this.prototype;
  $.each(Array.prototype.slice.call(arguments || []), function(properties) {
    Class.inherit(proto, properties);
  });
  return this;
};

var Event = {
  getPos: function(e, win) {
    // get mouse position
    win = win || window;
    e = e || win.event;
    var doc = win.document;
    doc = doc.html || doc.body;
    var page = {
      x: e.pageX || e.clientX + doc.scrollLeft,
      y: e.pageY || e.clientY + doc.scrollTop
    };
    return page;
  }
};

//common json manipulation methods
$jit.json = {

  /*
     Method: prune

     Clears all tree nodes having depth greater than maxLevel.

     Parameters:

        tree - A JSON tree object. For more information please see <Loader.loadJSON>.
        maxLevel - An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.

  */
  prune: function(tree, maxLevel) {
    this.each(tree, function(elem, i) {
      if (i == maxLevel && elem.children) {
        delete elem.children;
        elem.children = [];
      }
    });
  },

  /*
     Method: getParent

     Returns the parent node of the node having _id_ as id.

     Parameters:

        tree - A JSON tree object. See also <Loader.loadJSON>.
        id - The _id_ of the child node whose parent will be returned.

    Returns:

        A tree JSON node if any, or false otherwise.

  */
  getParent: function(tree, id) {
    if (tree.id == id)
      return false;
    var ch = tree.children;
    if (ch && ch.length > 0) {
      for ( var i = 0; i < ch.length; i++) {
        if (ch[i].id == id)
          return tree;
        else {
          var ans = this.getParent(ch[i], id);
          if (ans)
            return ans;
        }
      }
    }
    return false;
  },

  /*
     Method: getSubtree

     Returns the subtree that matches the given id.

     Parameters:

        tree - A JSON tree object. See also <Loader.loadJSON>.
        id - A node *unique* identifier.

     Returns:

        A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.

  */
  getSubtree: function(tree, id) {
    if (tree.id == id)
      return tree;
    for ( var i = 0, ch = tree.children; i < ch.length; i++) {
      var t = this.getSubtree(ch[i], id);
      if (t != null)
        return t;
    }
    return null;
  },

  /*
     Method: getLeaves

      Returns the leaves of the tree.

     Parameters:

        node - A JSON tree node. See also <Loader.loadJSON>.
        maxLevel - _optional_ A subtree's max level.

     Returns:

     An array having objects with two properties.

      - The _node_ property contains the leaf node.
      - The _level_ property specifies the depth of the node.

  */
  getLeaves: function(node, maxLevel) {
    var leaves = [], levelsToShow = maxLevel || Number.MAX_VALUE;
    this.each(node, function(elem, i) {
      if (i < levelsToShow && (!elem.children || elem.children.length == 0)) {
        leaves.push( {
          'node': elem,
          'level': levelsToShow - i
        });
      }
    });
    return leaves;
  },

  /*
     Method: eachLevel

      Iterates on tree nodes with relative depth less or equal than a specified level.

     Parameters:

        tree - A JSON tree or subtree. See also <Loader.loadJSON>.
        initLevel - An integer specifying the initial relative level. Usually zero.
        toLevel - An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.
        action - A function that receives a node and an integer specifying the actual level of the node.

    Example:
   (start code js)
     TreeUtil.eachLevel(tree, 0, 3, function(node, depth) {
        alert(node.name + ' ' + depth);
     });
   (end code)
  */
  eachLevel: function(tree, initLevel, toLevel, action) {
    if (initLevel <= toLevel) {
      action(tree, initLevel);
      for ( var i = 0, ch = tree.children; i < ch.length; i++) {
        this.eachLevel(ch[i], initLevel + 1, toLevel, action);
      }
    }
  },

  /*
     Method: each

      A tree iterator.

     Parameters:

        tree - A JSON tree or subtree. See also <Loader.loadJSON>.
        action - A function that receives a node.

    Example:
    (start code js)
      TreeUtil.each(tree, function(node) {
        alert(node.name);
      });
    (end code)

  */
  each: function(tree, action) {
    this.eachLevel(tree, 0, Number.MAX_VALUE, action);
  },

  /*
     Method: loadSubtrees

      Appends subtrees to leaves by requesting new subtrees
      with the _request_ method.

     Parameters:

        tree - A JSON tree node. <Loader.loadJSON>.
        controller - An object that implements a request method.

     Example:
      (start code js)
        TreeUtil.loadSubtrees(leafNode, {
          request: function(nodeId, level, onComplete) {
            //Pseudo-code to make an ajax request for a new subtree
            // that has as root id _nodeId_ and depth _level_ ...
            Ajax.request({
              'url': 'http://subtreerequesturl/',

              onSuccess: function(json) {
                onComplete.onComplete(nodeId, json);
              }
            });
          }
        });
      (end code)
  */
  loadSubtrees: function(tree, controller) {
    var maxLevel = controller.request && controller.levelsToShow;
    var leaves = this.getLeaves(tree, maxLevel), len = leaves.length, selectedNode = {};
    if (len == 0)
      controller.onComplete();
    for ( var i = 0, counter = 0; i < len; i++) {
      var leaf = leaves[i], id = leaf.node.id;
      selectedNode[id] = leaf.node;
      controller.request(id, leaf.level, {
        onComplete: function(nodeId, tree) {
          var ch = tree.children;
          selectedNode[nodeId].children = ch;
          if (++counter == len) {
            controller.onComplete();
          }
        }
      });
    }
  }
};
<|endoftext|>


<commit_before>angular.module("omniFactories")
    .factory("TransactionManager", ["$q", "userService", "TransactionGenerator", "WalletAssets", "TESTNET", "TX_DATA_URL",
        function TransactionManagerFactory($q, userService, TransactionGenerator, WalletAssets, TESTNET, TX_DATA_URL) {

            var TransactionManager = function(txType) {
                var self = this;

                self.initialize = function() {
                    self.txType = txType;
                }

                self.prepareData = function(rawdata, from) {
                    self.from = from;
                    var addressData = userService.getAddress(from);
                    var pubKey = null;
                    if (addressData.pubkey)
                        self.pubKey = addressData.pubkey.toUpperCase();
                    else {
                        self.privKey = new Bitcoin.ECKey.decodeEncryptedFormat(addressData.privkey, addressData.address); // Using address as temporary password
                        self.pubKey = self.privKey.getPubKeyHex();
                    }
                    rawdata['pubkey'] = self.pubKey;
                    rawdata['fee']=WalletAssets.minerFees;
                    rawdata['transaction_from'] = self.from;
                    rawdata['testnet'] = TESTNET || 0;
                    return rawdata; // followed by call to pushTransaction(data,pubkey);
                };

                self.prepareTransaction = function(unsignedTransactionHex, sourceScript) {

                    var bytes = Bitcoin.Util.hexToBytes(unsignedTransactionHex);
                    var transaction = Bitcoin.Transaction.deserialize(bytes);
                    var script = self.parseScript(sourceScript);

                    if (transaction.ins.length == 0) {
                        return {
                            waiting: false,
                            transactionError: true,
                            error: 'Error: Not enough inputs in the address!'
                        };
                    }
                    transaction.ins.forEach(function(input) {
                        input.script = script;
                    });
                    return transaction;
                }

                self.processTransaction = function(txData, signOffline) {
                    var deferred = $q.defer();
                    TransactionGenerator.getUnsignedTransaction(self.txType, txData).then(
                        function(successData) {
                            var successData = successData.data;
                            if (successData.status != 200 && successData.status != "OK") { /* Backwards compatibility for mastercoin-tools send API */
                                deferred.reject({
                                    waiting: false,
                                    transactionError: true,
                                    error: successData.error || successData.data, /* Backwards compatibility for mastercoin-tools send API */
                                    errorMessage: "Error preparing transaction"
                                });
                            } else {
                                var transaction = self.prepareTransaction(successData.unsignedhex || successData.transaction, successData.sourceScript)
                                if (signOffline) {
                                    var parsedBytes = transaction.serialize();

                                    TransactionGenerator.getArmoryUnsigned(Bitcoin.Util.bytesToHex(parsedBytes), pubKey).then(function(result) {
                                        deferred.resolve({
                                            unsignedTransaction: result.data.armoryUnsigned,
                                            waiting: false,
                                            readyToSign: true,
                                            unsaved: true
                                        });
                                    }, function(errorData) {
                                        deferred.reject({
                                            waiting: false,
                                            transactionError: true,
                                            error: errorData.message || errorData.data || 'Unknown Error',
                                            errorMessage:'Server error'
                                        });
                                    });
                                } else {
                                    try {
                                        //DEBUG console.log('before',transaction, Bitcoin.Util.bytesToHex(transaction.serialize()));
                                        var signedSuccess = transaction.signWithKey(self.privKey);

                                        var finalTransaction = Bitcoin.Util.bytesToHex(transaction.serialize());

                                        //Showing the user the transaction hash doesn't work right now
                                        //var transactionHash = Bitcoin.Util.bytesToHex(transaction.getHash().reverse());

                                        TransactionGenerator.pushSignedTransaction(finalTransaction).then(
                                            function(successData) {
                                                var successData = successData.data;
                                                if (successData.pushed.match(/submitted|success/gi) != null) {
                                                    deferred.resolve({
                                                        waiting: false,
                                                        transactionSuccess: true,
                                                        url : TX_DATA_URL + successData.tx;
                                                    })
                                                } else {
                                                    deferred.reject({
                                                        waiting: false,
                                                        transactionError: true,
                                                        error: successData.pushed, //Unspecified error, show user}
                                                        errorMessage: "Invalid transaction"
                                                    })
                                                }
                                            },
                                            function(errorData) {
                                                deferred.reject({
                                                    waiting: false,
                                                    transactionError: true,
                                                    error: errorData.message || errorData.data || 'Unknown Server Error',
                                                    errorMessage:'Server error'
                                                })
                                            });

                                        //DEBUG console.log(addressData, privKey, bytes, transaction, script, signedSuccess, finalTransaction );

                                    } catch (e) {
                                        deferred.reject({
                                            sendError: true,
                                            error: e.message || e.data || 'Unknown error',
                                            errorMessage:"Error sending transaction"
                                        })
                                    }
                                }
                            }
                        },
                        function(errorData) {
                            deferred.reject({
                                sendError: true,
                                error: errorData.message || errorData.data || 'Unknown Server Error',
                                errorMessage:'Server error'
                            })
                        });
                    return deferred.promise;
                };

                self.parseScript = function(script) {
                    var newScript = new Bitcoin.Script();
                    var s = script.split(" ");
                    for (var i = 0; i < s.length; i++) {
                        if (Bitcoin.Opcode.map.hasOwnProperty(s[i])) {
                            newScript.writeOp(Bitcoin.Opcode.map[s[i]]);
                        } else {
                            newScript.writeBytes(Bitcoin.Util.hexToBytes(s[i]));
                        }
                    }
                    return newScript;
                }

                self.initialize();
            }

            return TransactionManager;
    }]);<commit_msg>Fixed typo on TransactionManagerFactory<commit_after>angular.module("omniFactories")
    .factory("TransactionManager", ["$q", "userService", "TransactionGenerator", "WalletAssets", "TESTNET", "TX_DATA_URL",
        function TransactionManagerFactory($q, userService, TransactionGenerator, WalletAssets, TESTNET, TX_DATA_URL) {

            var TransactionManager = function(txType) {
                var self = this;

                self.initialize = function() {
                    self.txType = txType;
                }

                self.prepareData = function(rawdata, from) {
                    self.from = from;
                    var addressData = userService.getAddress(from);
                    var pubKey = null;
                    if (addressData.pubkey)
                        self.pubKey = addressData.pubkey.toUpperCase();
                    else {
                        self.privKey = new Bitcoin.ECKey.decodeEncryptedFormat(addressData.privkey, addressData.address); // Using address as temporary password
                        self.pubKey = self.privKey.getPubKeyHex();
                    }
                    rawdata['pubkey'] = self.pubKey;
                    rawdata['fee']=WalletAssets.minerFees;
                    rawdata['transaction_from'] = self.from;
                    rawdata['testnet'] = TESTNET || false;
                    return rawdata; // followed by call to pushTransaction(data,pubkey);
                };

                self.prepareTransaction = function(unsignedTransactionHex, sourceScript) {

                    var bytes = Bitcoin.Util.hexToBytes(unsignedTransactionHex);
                    var transaction = Bitcoin.Transaction.deserialize(bytes);
                    var script = self.parseScript(sourceScript);

                    if (transaction.ins.length == 0) {
                        return {
                            waiting: false,
                            transactionError: true,
                            error: 'Error: Not enough inputs in the address!'
                        };
                    }
                    transaction.ins.forEach(function(input) {
                        input.script = script;
                    });
                    return transaction;
                }

                self.processTransaction = function(txData, signOffline) {
                    var deferred = $q.defer();
                    TransactionGenerator.getUnsignedTransaction(self.txType, txData).then(
                        function(successData) {
                            var successData = successData.data;
                            if (successData.status != 200 && successData.status != "OK") { /* Backwards compatibility for mastercoin-tools send API */
                                deferred.reject({
                                    waiting: false,
                                    transactionError: true,
                                    error: successData.error || successData.data, /* Backwards compatibility for mastercoin-tools send API */
                                    errorMessage: "Error preparing transaction"
                                });
                            } else {
                                var transaction = self.prepareTransaction(successData.unsignedhex || successData.transaction, successData.sourceScript)
                                if (signOffline) {
                                    var parsedBytes = transaction.serialize();

                                    TransactionGenerator.getArmoryUnsigned(Bitcoin.Util.bytesToHex(parsedBytes), pubKey).then(function(result) {
                                        deferred.resolve({
                                            unsignedTransaction: result.data.armoryUnsigned,
                                            waiting: false,
                                            readyToSign: true,
                                            unsaved: true
                                        });
                                    }, function(errorData) {
                                        deferred.reject({
                                            waiting: false,
                                            transactionError: true,
                                            error: errorData.message || errorData.data || 'Unknown Error',
                                            errorMessage:'Server error'
                                        });
                                    });
                                } else {
                                    try {
                                        //DEBUG console.log('before',transaction, Bitcoin.Util.bytesToHex(transaction.serialize()));
                                        var signedSuccess = transaction.signWithKey(self.privKey);

                                        var finalTransaction = Bitcoin.Util.bytesToHex(transaction.serialize());

                                        //Showing the user the transaction hash doesn't work right now
                                        //var transactionHash = Bitcoin.Util.bytesToHex(transaction.getHash().reverse());

                                        TransactionGenerator.pushSignedTransaction(finalTransaction).then(
                                            function(successData) {
                                                var successData = successData.data;
                                                if (successData.pushed.match(/submitted|success/gi) != null) {
                                                    deferred.resolve({
                                                        waiting: false,
                                                        transactionSuccess: true,
                                                        url : TX_DATA_URL + successData.tx
                                                    })
                                                } else {
                                                    deferred.reject({
                                                        waiting: false,
                                                        transactionError: true,
                                                        error: successData.pushed, //Unspecified error, show user}
                                                        errorMessage: "Invalid transaction"
                                                    })
                                                }
                                            },
                                            function(errorData) {
                                                deferred.reject({
                                                    waiting: false,
                                                    transactionError: true,
                                                    error: errorData.message || errorData.data || 'Unknown Server Error',
                                                    errorMessage:'Server error'
                                                })
                                            });

                                        //DEBUG console.log(addressData, privKey, bytes, transaction, script, signedSuccess, finalTransaction );

                                    } catch (e) {
                                        deferred.reject({
                                            sendError: true,
                                            error: e.message || e.data || 'Unknown error',
                                            errorMessage:"Error sending transaction"
                                        })
                                    }
                                }
                            }
                        },
                        function(errorData) {
                            deferred.reject({
                                sendError: true,
                                error: errorData.message || errorData.data || 'Unknown Server Error',
                                errorMessage:'Server error'
                            })
                        });
                    return deferred.promise;
                };

                self.parseScript = function(script) {
                    var newScript = new Bitcoin.Script();
                    var s = script.split(" ");
                    for (var i = 0; i < s.length; i++) {
                        if (Bitcoin.Opcode.map.hasOwnProperty(s[i])) {
                            newScript.writeOp(Bitcoin.Opcode.map[s[i]]);
                        } else {
                            newScript.writeBytes(Bitcoin.Util.hexToBytes(s[i]));
                        }
                    }
                    return newScript;
                }

                self.initialize();
            }

            return TransactionManager;
    }]);<|endoftext|>