<commit_before>angular.module('courseTable')
    .component('courseTable', {
        templateUrl: '/partial/course-table',
        controller: ['$scope', '$http', '$q', function CourseTableController($scope, $http, $q) {
            let self = this;

            // Set when an unexpted error occurs
            this.criticalError = null;

            // Create as empty arrays so that one input will be created for the
            // user to start off with
            this.institutions = [''];
            this.input = [''];

            // Input that has had already been sent to the API
            this.displayedInput = [];
            this.displayedInstitutions = [];

            // Used to validate course inputs. Lenient about spaces and case
            this.courseRegex = /^ *[A-Z]{3} +[0-9]{3} *$/i;

            // 2D array representing table data. Initialize it with two empty
            // cells on the first row
            this.data = [[{}, {}]];

            // List of all API-provided institutions
            this.availableInstitutions = [];

            /** Copied from app/util.js */
            this.nbsp = String.fromCharCode(160);

            /**
             * Copied from app/util.js
             *
             * Replaces all sequences of new line, nbsp, and space characters with a
             * single space and trims.
             */
            this.normalizeWhitespace = function(text) {
                return text.replace(new RegExp(`(?:\r\n|\r|\n|${self.nbsp}| )+`, 'g'), ' ').trim();
            };

            /**
             * Displays the data from /api/course/:courses/:institution where
             * :courses is course located at input[rowIndex] and :institutions
             * is a comma-separated list of selected institutions.
             */
            this.populateRow = function(rowIndex) {
                let url = '/api/course/' +
                        encodeURIComponent(self.displayedInput[rowIndex]) +
                        '/' + self.joinValidInstitutions();

                $http.get(url).then(function(data) {
                    data = data.data;

                    // Reset
                    self.data[rowIndex] = [];
                    let groupedEquivs = _.groupBy(data.equivalencies, 'institution');

                    for (let i = 0; i < self.institutions.length; i++) {
                        let institution = self.institutions[i];
                        if (institution.trim() === '')
                            continue;

                        // No real reason for this variable other than semantics
                        let columnIndex = i;

                        if (!(institution in groupedEquivs)) {
                            // No equivalencies found
                            self.data[rowIndex][columnIndex] = [self.prepareUnknownEquivalency()]
                        } else {
                            let equivs = groupedEquivs[institution];

                            // Assign the data to its specific location
                            self.data[rowIndex][columnIndex] = [];
                            for (let equiv of equivs) {
                                self.data[rowIndex][columnIndex].push(self.prepareEquivalency(equiv));
                            }
                        }

                        self.fillEmptyCellsInRow(rowIndex);
                    }
                }).catch(function(err) {
                    $ctrl.criticalError = 'Unable to complete the request'
                    console.error(err);
                });
            };

            /**
             * Populates a column with data from
             * /api/institution/:institution/:courses where :institution is the
             * value at displayedInstitutions[columnIndex] and :courses is a
             * comma-separated list of courses from displayedInput
             */
            this.populateColumn = function(columnIndex) {
                let joinedCourses = self.joinValidCourses();
                if (!joinedCourses)
                    return;

                let url = '/api/institution/' +
                        encodeURIComponent(self.displayedInstitutions[columnIndex]) +
                        '/' + joinedCourses;

                $http.get(url).then(function(data) {
                    data = data.data;

                    for (let i = 0; i < self.displayedInput.length; i++) {
                        let course = self.displayedInput[i];
                        // Hasn't been filled out yet
                        if (!course)
                            continue;

                        let rowIndex = _.findIndex(self.input, o => o && o.toUpperCase() === course.toUpperCase());

                        // Find the appropriate equivalency list for the course
                        // from the data
                        let equivalencyList = _.find(data.courses,
                            c => c.subject + ' ' + c.number === self.normalizeWhitespace(course));

                        if (!equivalencyList) {
                            // No equivalencies for this couse
                            self.data[rowIndex][columnIndex] = [self.prepareUnknownEquivalency()];
                        } else {
                            equivalencyList = equivalencyList.equivalencies;

                            // Assign the data to its specific location
                            self.data[rowIndex][columnIndex] = [];
                            for (let equiv of equivalencyList) {
                                self.data[rowIndex][columnIndex].push(self.prepareEquivalency(equiv));
                            }
                        }
                    }
                }).catch(function(err) {
                    $ctrl.criticalError = 'Unable to complete the request'
                    console.error(err);
                });
            };

            /** Adds another <select> to choose an institution */
            this.addInstitution = function() {
                this.institutions.push('');
                this.fillEmptyCellsInColumn(this.institutions.length - 1);
            };

            /** Adds another <input> for course entry */
            this.addInputCourse = function() {
                this.input.push(null);
                this.fillEmptyCellsInRow(this.input.length - 1);
            };

            /**
             * Populates undefined cells in data[rowIndex] with an empty array
             * so that Angular renders an empty cell
             */
            this.fillEmptyCellsInRow = function(rowIndex) {
                if (!self.data[rowIndex])
                    self.data[rowIndex] = [];
                // Fill in with blank data
                for (let j = 0; j < self.institutions.length; j++)
                    if (!self.data[rowIndex][j])
                        self.data[rowIndex][j] = []
            };

            /** Column version of fillEmptyCellsInRow() */
            this.fillEmptyCellsInColumn = function(columnIndex) {
                for (let j = 0; j < self.input.length; j++)
                    if (!self.data[j][columnIndex])
                        self.data[j][columnIndex] = [];
            }

            /**
             * Join non-empty institutions by a comma to avoid bad API calls
             * when empty cells are present
             */
            this.joinValidInstitutions = function() {
                return _.join(_.filter(this.institutions, o => o.trim() !== ''));
            };

            /** Input version of joinValidInstitutions() */
            this.joinValidCourses = function() {
                return _.join(_.filter(this.displayedInput, o => o && o.trim() !== ''));
            }

            /**
             * Transforms an equivalency to a format that is expected by the
             * template. Resulting object contains two entries.
             *
             * 1. 'muted': The equivalencies input array but with the first
             *     course dropped. Displayed with a 'text-muted' CSS class,
             *     hence the name.
             * 2. 'normal': All output courses
             */
            this.prepareEquivalency = function(equiv) {
                return {
                    muted: this.formatCourseArray(_.drop(equiv.input)),
                    normal: this.formatCourseArray(equiv.output)
                };
            };

            /**
             * Returns {danger: true}. Used by the template to detect when a
             * course has no equivalencies.
             */
            this.prepareUnknownEquivalency = function() {
                return {danger: true};
            }

            this.formatCourseArray = function(courses) {
                return _.join(_.map(courses, c => `${c.subject} ${c.number} (${self.formatCredits(c.credits)} credits)`), ', ');
            };

            this.formatCredits = function(credits) {
                if (credits === -1 || credits === -2) return '?';
                return credits;
            }

            this.inputName = function(index) { return 'input' + index; };
            this.selectName = function(index) { return 'institution' + index; };

            /**
             * Create an object that can be passed to ngClass
             *
             * @param form The table form from course-table.template.pug
             * @param index The index of the <input> within the form
             * @param dangerClass The name of the class to applied for an error
             * @param warningClass The name of the class to applied for a warning
             */
            let createStylesObject = function(form, index, dangerClass, warningClass) {
                let styles = {};
                let control = form[self.inputName(index)];

                if (control.$touched) {
                    if (control.$error.required) {
                        // Only warning empty
                        styles[warningClass] = true;
                    } else {
                        // Whine otherwise
                        styles[dangerClass] = control.$invalid;
                    }
                }
                return styles;
            }

            /**
             * Creates a styles object using createStylesObject() for <input>
             * elements.
             */
            this.createInputStyleObject = function(form, index) {
                return createStylesObject(form, index, 'has-error', 'has-warning')
            }

            /**
             * createInputStyleObject() but for <tr> elements
             */
            this.createRowStyleObject = function(form, index) {
                return createStylesObject(form, index, 'danger', 'warning')
            }

            // Dynamically get a list of all institutions
            $http.get('/api/institutions').then(function(data) {
                self.availableInstitutions = data.data;
                self.institutions = [self.availableInstitutions[0].acronym, ''];
            });

            // Deep-watch input courses to populate a row when a course
            // becomes valid
            $scope.$watchCollection('$ctrl.input', function(newVal, oldVal) {
                // Form scope hasn't been registered yet
                if (!$scope.tableForm)
                    return;

                for (let i = 0; i < self.input.length; i++) {
                    let inputCourse = self.input[i];
                    let inputElement = $scope.tableForm[self.inputName(i)];

                    // Form scope hasn't been registered yet
                    if (!inputElement)
                        break;

                    if (inputElement.$invalid) {
                        self.data[i] = [];
                        for (let j = 0; j < self.institutions.length; j++)
                            self.data[i].push([]);
                        self.displayedInput[i] = null;
                        continue;
                    }

                    // $modelValue is only set when it's valid
                    if (!inputElement.$modelValue)
                        continue;

                    let value = inputElement.$modelValue.toUpperCase();

                    if (inputElement.$valid && value !== self.displayedInput[i]) {
                        self.displayedInput[i] = value;
                        self.populateRow(i);
                    }
                }
            });

            // Do the same as the previous $watchCollection, but for
            // institutions and columns
            $scope.$watchCollection('$ctrl.institutions', function(newVal, oldVal) {
                // No scope registered yet
                if (!$scope.tableForm)
                    return;

                for (let i = 0; i < self.institutions.length; i++) {
                    let institution = self.institutions[i];
                    let selectElement = $scope.tableForm[self.selectName(i)];

                    if (!selectElement)
                        break;

                    // if (!selectElement.$modelValue)
                    //     continue;

                    let value = selectElement.$modelValue;

                    if (selectElement.$valid && value !== self.displayedInstitutions[i]) {
                        self.displayedInstitutions[i] = value;
                        self.populateColumn(i);
                    }
                }
            });

            // Automatically add new input rows when the last <input> has text
            $scope.$watch(function() {
                // Find the last <input> available in the form
                if (self.input.length === 0) return 'NO INPUTS';
                let input = $scope.tableForm[self.inputName(self.input.length - 1)];
                if (!input) return undefined;

                // Use $$lastCommittedViewValue rather than $viewValue because
                // $viewValue has to pass through validation before it gets set,
                // we care about the raw input.
                return input['$$lastCommittedViewValue'];
            }, function(newVal, oldVal) {
                if (!newVal)
                    return;

                // oldVal will either be null or an empty string when the
                // new course needs to be created
                if ((oldVal === '' || oldVal === null) && newVal !== '') {
                    self.addInputCourse();
                }
            })
        }],
    });
<commit_msg>Automatically capitalize course input<commit_after>angular.module('courseTable')
    .component('courseTable', {
        templateUrl: '/partial/course-table',
        controller: ['$scope', '$http', '$q', function CourseTableController($scope, $http, $q) {
            let self = this;

            // Set when an unexpted error occurs
            this.criticalError = null;

            // Create as empty arrays so that one input will be created for the
            // user to start off with
            this.institutions = [''];
            this.input = [''];

            // Input that has had already been sent to the API
            this.displayedInput = [];
            this.displayedInstitutions = [];

            // Used to validate course inputs. Lenient about spaces and case
            this.courseRegex = /^ *[A-Z]{3} +[0-9]{3} *$/i;

            // 2D array representing table data. Initialize it with two empty
            // cells on the first row
            this.data = [[{}, {}]];

            // List of all API-provided institutions
            this.availableInstitutions = [];

            /** Copied from app/util.js */
            this.nbsp = String.fromCharCode(160);

            /**
             * Copied from app/util.js
             *
             * Replaces all sequences of new line, nbsp, and space characters with a
             * single space and trims.
             */
            this.normalizeWhitespace = function(text) {
                return text.replace(new RegExp(`(?:\r\n|\r|\n|${self.nbsp}| )+`, 'g'), ' ').trim();
            };

            /**
             * Displays the data from /api/course/:courses/:institution where
             * :courses is course located at input[rowIndex] and :institutions
             * is a comma-separated list of selected institutions.
             */
            this.populateRow = function(rowIndex) {
                let url = '/api/course/' +
                        encodeURIComponent(self.displayedInput[rowIndex]) +
                        '/' + self.joinValidInstitutions();

                $http.get(url).then(function(data) {
                    data = data.data;

                    // Reset
                    self.data[rowIndex] = [];
                    let groupedEquivs = _.groupBy(data.equivalencies, 'institution');

                    for (let i = 0; i < self.institutions.length; i++) {
                        let institution = self.institutions[i];
                        if (institution.trim() === '')
                            continue;

                        // No real reason for this variable other than semantics
                        let columnIndex = i;

                        if (!(institution in groupedEquivs)) {
                            // No equivalencies found
                            self.data[rowIndex][columnIndex] = [self.prepareUnknownEquivalency()]
                        } else {
                            let equivs = groupedEquivs[institution];

                            // Assign the data to its specific location
                            self.data[rowIndex][columnIndex] = [];
                            for (let equiv of equivs) {
                                self.data[rowIndex][columnIndex].push(self.prepareEquivalency(equiv));
                            }
                        }

                        self.fillEmptyCellsInRow(rowIndex);
                    }
                }).catch(function(err) {
                    $ctrl.criticalError = 'Unable to complete the request'
                    console.error(err);
                });
            };

            /**
             * Populates a column with data from
             * /api/institution/:institution/:courses where :institution is the
             * value at displayedInstitutions[columnIndex] and :courses is a
             * comma-separated list of courses from displayedInput
             */
            this.populateColumn = function(columnIndex) {
                let joinedCourses = self.joinValidCourses();
                if (!joinedCourses)
                    return;

                let url = '/api/institution/' +
                        encodeURIComponent(self.displayedInstitutions[columnIndex]) +
                        '/' + joinedCourses;

                $http.get(url).then(function(data) {
                    data = data.data;

                    for (let i = 0; i < self.displayedInput.length; i++) {
                        let course = self.displayedInput[i];
                        // Hasn't been filled out yet
                        if (!course)
                            continue;

                        let rowIndex = _.findIndex(self.input, o => o && o.toUpperCase() === course.toUpperCase());

                        // Find the appropriate equivalency list for the course
                        // from the data
                        let equivalencyList = _.find(data.courses,
                            c => c.subject + ' ' + c.number === self.normalizeWhitespace(course));

                        if (!equivalencyList) {
                            // No equivalencies for this couse
                            self.data[rowIndex][columnIndex] = [self.prepareUnknownEquivalency()];
                        } else {
                            equivalencyList = equivalencyList.equivalencies;

                            // Assign the data to its specific location
                            self.data[rowIndex][columnIndex] = [];
                            for (let equiv of equivalencyList) {
                                self.data[rowIndex][columnIndex].push(self.prepareEquivalency(equiv));
                            }
                        }
                    }
                }).catch(function(err) {
                    $ctrl.criticalError = 'Unable to complete the request'
                    console.error(err);
                });
            };

            /** Adds another <select> to choose an institution */
            this.addInstitution = function() {
                this.institutions.push('');
                this.fillEmptyCellsInColumn(this.institutions.length - 1);
            };

            /** Adds another <input> for course entry */
            this.addInputCourse = function() {
                this.input.push(null);
                this.fillEmptyCellsInRow(this.input.length - 1);
            };

            /**
             * Populates undefined cells in data[rowIndex] with an empty array
             * so that Angular renders an empty cell
             */
            this.fillEmptyCellsInRow = function(rowIndex) {
                if (!self.data[rowIndex])
                    self.data[rowIndex] = [];
                // Fill in with blank data
                for (let j = 0; j < self.institutions.length; j++)
                    if (!self.data[rowIndex][j])
                        self.data[rowIndex][j] = []
            };

            /** Column version of fillEmptyCellsInRow() */
            this.fillEmptyCellsInColumn = function(columnIndex) {
                for (let j = 0; j < self.input.length; j++)
                    if (!self.data[j][columnIndex])
                        self.data[j][columnIndex] = [];
            }

            /**
             * Join non-empty institutions by a comma to avoid bad API calls
             * when empty cells are present
             */
            this.joinValidInstitutions = function() {
                return _.join(_.filter(this.institutions, o => o.trim() !== ''));
            };

            /** Input version of joinValidInstitutions() */
            this.joinValidCourses = function() {
                return _.join(_.filter(this.displayedInput, o => o && o.trim() !== ''));
            }

            /**
             * Transforms an equivalency to a format that is expected by the
             * template. Resulting object contains two entries.
             *
             * 1. 'muted': The equivalencies input array but with the first
             *     course dropped. Displayed with a 'text-muted' CSS class,
             *     hence the name.
             * 2. 'normal': All output courses
             */
            this.prepareEquivalency = function(equiv) {
                return {
                    muted: this.formatCourseArray(_.drop(equiv.input)),
                    normal: this.formatCourseArray(equiv.output)
                };
            };

            /**
             * Returns {danger: true}. Used by the template to detect when a
             * course has no equivalencies.
             */
            this.prepareUnknownEquivalency = function() {
                return {danger: true};
            }

            this.formatCourseArray = function(courses) {
                return _.join(_.map(courses, c => `${c.subject} ${c.number} (${self.formatCredits(c.credits)} credits)`), ', ');
            };

            this.formatCredits = function(credits) {
                if (credits === -1 || credits === -2) return '?';
                return credits;
            }

            this.inputName = function(index) { return 'input' + index; };
            this.selectName = function(index) { return 'institution' + index; };

            /**
             * Create an object that can be passed to ngClass
             *
             * @param form The table form from course-table.template.pug
             * @param index The index of the <input> within the form
             * @param dangerClass The name of the class to applied for an error
             * @param warningClass The name of the class to applied for a warning
             */
            let createStylesObject = function(form, index, dangerClass, warningClass) {
                let styles = {};
                let control = form[self.inputName(index)];

                if (control.$touched) {
                    if (control.$error.required) {
                        // Only warning empty
                        styles[warningClass] = true;
                    } else {
                        // Whine otherwise
                        styles[dangerClass] = control.$invalid;
                    }
                }
                return styles;
            }

            /**
             * Creates a styles object using createStylesObject() for <input>
             * elements.
             */
            this.createInputStyleObject = function(form, index) {
                return createStylesObject(form, index, 'has-error', 'has-warning')
            }

            /**
             * createInputStyleObject() but for <tr> elements
             */
            this.createRowStyleObject = function(form, index) {
                return createStylesObject(form, index, 'danger', 'warning')
            }

            // Dynamically get a list of all institutions
            $http.get('/api/institutions').then(function(data) {
                self.availableInstitutions = data.data;
                self.institutions = [self.availableInstitutions[0].acronym, ''];
            });

            // Deep-watch input courses to populate a row when a course
            // becomes valid
            $scope.$watchCollection('$ctrl.input', function(newVal, oldVal) {
                // Form scope hasn't been registered yet
                if (!$scope.tableForm)
                    return;

                for (let i = 0; i < self.input.length; i++) {
                    let inputCourse = self.input[i];
                    let inputElement = $scope.tableForm[self.inputName(i)];

                    // Form scope hasn't been registered yet
                    if (!inputElement)
                        break;

                    if (inputElement.$invalid) {
                        self.data[i] = [];
                        for (let j = 0; j < self.institutions.length; j++)
                            self.data[i].push([]);
                        self.displayedInput[i] = null;
                        continue;
                    }

                    // $modelValue is only set when it's valid
                    if (!inputElement.$modelValue)
                        continue;

                    let value = inputElement.$modelValue.toUpperCase();

                    if (inputElement.$valid && value !== self.displayedInput[i]) {
                        self.displayedInput[i] = value;
                        self.input[i] = value;
                        self.populateRow(i);
                    }
                }
            });

            // Do the same as the previous $watchCollection, but for
            // institutions and columns
            $scope.$watchCollection('$ctrl.institutions', function(newVal, oldVal) {
                // No scope registered yet
                if (!$scope.tableForm)
                    return;

                for (let i = 0; i < self.institutions.length; i++) {
                    let institution = self.institutions[i];
                    let selectElement = $scope.tableForm[self.selectName(i)];

                    if (!selectElement)
                        break;

                    // if (!selectElement.$modelValue)
                    //     continue;

                    let value = selectElement.$modelValue;

                    if (selectElement.$valid && value !== self.displayedInstitutions[i]) {
                        self.displayedInstitutions[i] = value;
                        self.populateColumn(i);
                    }
                }
            });

            // Automatically add new input rows when the last <input> has text
            $scope.$watch(function() {
                // Find the last <input> available in the form
                if (self.input.length === 0) return 'NO INPUTS';
                let input = $scope.tableForm[self.inputName(self.input.length - 1)];
                if (!input) return undefined;

                // Use $$lastCommittedViewValue rather than $viewValue because
                // $viewValue has to pass through validation before it gets set,
                // we care about the raw input.
                return input['$$lastCommittedViewValue'];
            }, function(newVal, oldVal) {
                if (!newVal)
                    return;

                // oldVal will either be null or an empty string when the
                // new course needs to be created
                if ((oldVal === '' || oldVal === null) && newVal !== '') {
                    self.addInputCourse();
                }
            })
        }],
    });
<|endoftext|>


<commit_before>"use strict";

var Config = {
  supportsHistory : !!(window.history && history.pushState),
  inMaintenanceMode : false,

  treeName: 'mozilla-central',
  hgBaseURL: "https://hg.mozilla.org/",
  hgURL: "https://hg.mozilla.org/mozilla-central/",
  hgRevURL: "https://hg.mozilla.org/mozilla-central/rev/",
  hgPushlogURL: "https://hg.mozilla.org/mozilla-central/pushloghtml?changeset=",
  showBugURL: "https://bugzilla.mozilla.org/show_bug.cgi?id=",
  productionURL: "https://tbpl.mozilla.org/mcmerge/",

  // Here be dragons
  versionRE: /^mozilla\d+$/i,
  csetInputRE: /^(tip|[\da-f]{12,40})$/i,
  csetIDRE: /\b([\da-f]{12,40})\b/ig,
  leaveOpenRE: /(?:leave|keep)(?:-|\s+)open/i,
  bugNumRE: /\b(\d{4,7})\b/g,
  strictBugNumRE: /^(\d{4,7})$/,
  mergeRE: /\bmerg(ed?|ing)\b/i,
  backoutRE: /back(ing|ed)?\s*out/i,
  revertRE: /revert(ing)?/i,
  partialRevertRE: /(?:from|in)(?:\s+(?:rev(?:ision)?|c(?:hange)?set))?\s+([\da-f]{12,40})/i,
  partialTestRE: /test\s+for/i,
  revertRangeRE: /revert(?:ing)?\s+(?:\S+\s+)?to(?:\s+(?:rev(?:ision)?|c(?:hange)set))?\s+([\da-f]{12,40})/i,
  csetRangeRE: /\b([\da-f]{12,40})\s*(?:to|:|-|through)\s*([\da-f]{12,40})/i,
  hgRevRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\//ig,
  hgRevFullRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\/[\da-f]{12}/ig,
  hgPushlogRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/pushloghtml\?changeset=/ig,
  emailRE: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,   // I'm not looking for RFC 822 compliance here!

  // The many ways bug numbers are specified
  bugRE1: /b(?:ug)?=(\d{4,7})\b/i,                       // e.g. b=XXXXXX
  bugRE2: /^fix(?:es)?\s*(?:for\s*)?(\d{4,7})\b/i,                         // Fix is sometimes used as a synonym for bug
  bugRE3: /to\s+fix\s+bug\s+(\d{4,7})/i,
  bugRE4: /\((\d{4,7}), r=/i,                          // e.g. JS-team style (XXXXXX, r=foo)
  bugRE5: /but\s*(\d{4,7})/i,                          // The typo but XXXXXX happens quite often
  bugRE6: /\bb(?:u?g(?:zilla)?)?:?\s*#?(\d{4,7})\b/i,  // The catchall
  bugRE7: /^(\d{4,7})\b/i,                              // e.g. XXXXXX,

  repoMergeRE: "\\s+(&|and|with|<->|<>|-?->|(in|on)?to)\\s+",

  mcSynonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"],

  rewriteTrees: {
    "thunderbird-trunk": "comm-central",
    "thunderbird-aurora": "comm-aurora",
    "thunderbird-beta": "comm-beta",
    "thunderbird-esr31": "comm-esr31",
    "thunderbird-esr24": "comm-esr24",
  },

  treeInfo: {
    "mozilla-central": {
      repo: "mozilla-central",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"]
    },
    "b2g-inbound": {
      repo: "integration/b2g-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["b2g-inbound", "b-i", "bi"]
    },
    "mozilla-inbound": {
      repo: "integration/mozilla-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["mozilla-inbound", "inbound", "m-i", "mi"]
    },
    "mozilla-aurora": {
      repo: "releases/mozilla-aurora",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-aurora", "mozilla-aurora"]
    },
    "mozilla-beta": {
      repo: "releases/mozilla-beta",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-beta", "mozilla-beta"]
    },
    "mozilla-release": {
      repo: "releases/mozilla-release",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-release", "mozilla-release"]
    },
    "mozilla-esr31": {
      repo: "releases/mozilla-esr31",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr31", "releases/mozilla-esr31", "mozilla-esr31"]
    },
    "mozilla-esr24": {
      repo: "releases/mozilla-esr24",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr24", "releases/mozilla-esr24", "mozilla-esr24"]
    },
    "mozilla-b2g30-v1.4": {
      repo: "releases/mozilla-b2g30_v1_4",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g30", "mozilla-b2g30", "mozilla-b2g30_v1_4", "v1.4"]
    },
    "mozilla-b2g28-v1.3": {
      repo: "releases/mozilla-b2g28_v1_3",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28", "mozilla-b2g28", "mozilla-b2g28_v1_3", "v1.3"]
    },
    "mozilla-b2g28-v1.3t": {
      repo: "releases/mozilla-b2g28_v1_3t",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28t", "mozilla-b2g28t", "mozilla-b2g28_v1_3t", "v1.3t"]
    },
    "jetpack": {
      repo: "projects/addon-sdk",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["projects/addon-sdk", "addon-sdk"]
    },
    "build-system": {
      repo: "projects/build-system",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["build-system", "b-s", "bs"]
    },
    "fx-team": {
      repo: "integration/fx-team",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["fx-team"],
      additions: "[fixed-in-fx-team]"
    },
    "graphics": {
      repo: "projects/graphics",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/graphics", "graphics"]
    },
    "services-central": {
      repo: "services/services-central",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["services-central", "s-c", "sc", "services/services-central"],
      additions: "[fixed in services]"
    },
    "ux": {
      unconditionalFlag: false,
      repo: "projects/ux",
      synonyms: ["ux", "projects/ux"]
    },
    "alder": {
      repo: "projects/alder",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["alder", "projects/alder"]
    },
    "ash": {
      repo: "projects/ash",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["ash", "projects/ash"]
    },
    "birch": {
      repo: "projects/birch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/birch", "birch"]
    },
    "cedar": {
      repo: "projects/cedar",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cedar", "cedar"]
    },
    "cypress": {
      repo: "projects/cypress",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cypress", "cypress"]
    },
    "date": {
      repo: "projects/date",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/date", "date"]
    },
    "elm": {
      repo: "projects/elm",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/elm", "elm"]
    },
    "fig": {
      repo: "projects/fig",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/fig", "fig"],
      additions: "[fixed-in-fig]"
    },
    "gum": {
      repo: "projects/gum",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/gum", "gum"]
    },
    "holly": {
      repo: "projects/holly",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/holly", "holly"]
    },
    "jamun": {
      repo: "projects/jamun",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/jamun", "jamun"]
    },
    "larch": {
      repo: "projects/larch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/larch", "larch"]
    },
    "maple": {
      repo: "projects/maple",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/maple", "maple"]
    },
    "oak": {
      repo: "projects/oak",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/oak", "oak"]
    },
    "pine": {
      repo: "projects/pine",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/pine", "pine"]
    },
    "comm-central": {
      repo: "comm-central",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["comm-central", "c-c"]
    },
    "comm-aurora": {
      repo: "releases/comm-aurora",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-aurora", "comm-aurora"]
    },
    "comm-beta": {
      repo: "releases/comm-beta",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-beta", "comm-beta"]
    },
    "comm-esr31": {
      repo: "releases/comm-esr31",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr31", "comm-esr31"]
    },
    "comm-esr24": {
      repo: "releases/comm-esr24",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr24", "comm-esr24", "comm-release"]
    }
  }
};

Config.bugNumberREs = [Config.bugRE1, Config.bugRE2, Config.bugRE3,
                       Config.bugRE4, Config.bugRE5, Config.bugRE6, Config.bugRE7];

(function () {
  var base = Config.hgBaseURL;
  for (var treeName in Config.treeInfo) {
    var repo = Config.treeInfo[treeName].repo;
    Config.treeInfo[treeName]['hgURL'] = base + repo + '/';
    Config.treeInfo[treeName]['hgRevURL'] = base + repo + '/rev/';
    Config.treeInfo[treeName]['hgPushlogURL'] = base + repo + '/pushloghtml?changeset=';
    Config.treeInfo[treeName]['hgRevRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/', 'ig');
    Config.treeInfo[treeName]['hgRevFullRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/[\\da-f]{12}', 'ig');
    Config.treeInfo[treeName]['hgPushlogRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/pushloghtml\\?changeset=', 'ig');
  }
})();

// Detect input type="email" support
$(document).ready(function() {
  Config.needsValidation = $('#username').prop('type') != 'email';
});
<commit_msg>bug 1037107 - mozilla-b2g32_v2_0. r=ryanvm<commit_after>"use strict";

var Config = {
  supportsHistory : !!(window.history && history.pushState),
  inMaintenanceMode : false,

  treeName: 'mozilla-central',
  hgBaseURL: "https://hg.mozilla.org/",
  hgURL: "https://hg.mozilla.org/mozilla-central/",
  hgRevURL: "https://hg.mozilla.org/mozilla-central/rev/",
  hgPushlogURL: "https://hg.mozilla.org/mozilla-central/pushloghtml?changeset=",
  showBugURL: "https://bugzilla.mozilla.org/show_bug.cgi?id=",
  productionURL: "https://tbpl.mozilla.org/mcmerge/",

  // Here be dragons
  versionRE: /^mozilla\d+$/i,
  csetInputRE: /^(tip|[\da-f]{12,40})$/i,
  csetIDRE: /\b([\da-f]{12,40})\b/ig,
  leaveOpenRE: /(?:leave|keep)(?:-|\s+)open/i,
  bugNumRE: /\b(\d{4,7})\b/g,
  strictBugNumRE: /^(\d{4,7})$/,
  mergeRE: /\bmerg(ed?|ing)\b/i,
  backoutRE: /back(ing|ed)?\s*out/i,
  revertRE: /revert(ing)?/i,
  partialRevertRE: /(?:from|in)(?:\s+(?:rev(?:ision)?|c(?:hange)?set))?\s+([\da-f]{12,40})/i,
  partialTestRE: /test\s+for/i,
  revertRangeRE: /revert(?:ing)?\s+(?:\S+\s+)?to(?:\s+(?:rev(?:ision)?|c(?:hange)set))?\s+([\da-f]{12,40})/i,
  csetRangeRE: /\b([\da-f]{12,40})\s*(?:to|:|-|through)\s*([\da-f]{12,40})/i,
  hgRevRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\//ig,
  hgRevFullRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\/[\da-f]{12}/ig,
  hgPushlogRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/pushloghtml\?changeset=/ig,
  emailRE: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,   // I'm not looking for RFC 822 compliance here!

  // The many ways bug numbers are specified
  bugRE1: /b(?:ug)?=(\d{4,7})\b/i,                       // e.g. b=XXXXXX
  bugRE2: /^fix(?:es)?\s*(?:for\s*)?(\d{4,7})\b/i,                         // Fix is sometimes used as a synonym for bug
  bugRE3: /to\s+fix\s+bug\s+(\d{4,7})/i,
  bugRE4: /\((\d{4,7}), r=/i,                          // e.g. JS-team style (XXXXXX, r=foo)
  bugRE5: /but\s*(\d{4,7})/i,                          // The typo but XXXXXX happens quite often
  bugRE6: /\bb(?:u?g(?:zilla)?)?:?\s*#?(\d{4,7})\b/i,  // The catchall
  bugRE7: /^(\d{4,7})\b/i,                              // e.g. XXXXXX,

  repoMergeRE: "\\s+(&|and|with|<->|<>|-?->|(in|on)?to)\\s+",

  mcSynonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"],

  rewriteTrees: {
    "thunderbird-trunk": "comm-central",
    "thunderbird-aurora": "comm-aurora",
    "thunderbird-beta": "comm-beta",
    "thunderbird-esr31": "comm-esr31",
    "thunderbird-esr24": "comm-esr24",
  },

  treeInfo: {
    "mozilla-central": {
      repo: "mozilla-central",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"]
    },
    "b2g-inbound": {
      repo: "integration/b2g-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["b2g-inbound", "b-i", "bi"]
    },
    "mozilla-inbound": {
      repo: "integration/mozilla-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["mozilla-inbound", "inbound", "m-i", "mi"]
    },
    "mozilla-aurora": {
      repo: "releases/mozilla-aurora",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-aurora", "mozilla-aurora"]
    },
    "mozilla-beta": {
      repo: "releases/mozilla-beta",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-beta", "mozilla-beta"]
    },
    "mozilla-release": {
      repo: "releases/mozilla-release",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-release", "mozilla-release"]
    },
    "mozilla-esr31": {
      repo: "releases/mozilla-esr31",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr31", "releases/mozilla-esr31", "mozilla-esr31"]
    },
    "mozilla-esr24": {
      repo: "releases/mozilla-esr24",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr24", "releases/mozilla-esr24", "mozilla-esr24"]
    },
    "mozilla-b2g32-v2.0": {
      repo: "releases/mozilla-b2g32_v2_0",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g32", "mozilla-b2g32", "mozilla-b2g32_v2_0", "v2.0"]
    },
    "mozilla-b2g30-v1.4": {
      repo: "releases/mozilla-b2g30_v1_4",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g30", "mozilla-b2g30", "mozilla-b2g30_v1_4", "v1.4"]
    },
    "mozilla-b2g28-v1.3": {
      repo: "releases/mozilla-b2g28_v1_3",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28", "mozilla-b2g28", "mozilla-b2g28_v1_3", "v1.3"]
    },
    "mozilla-b2g28-v1.3t": {
      repo: "releases/mozilla-b2g28_v1_3t",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28t", "mozilla-b2g28t", "mozilla-b2g28_v1_3t", "v1.3t"]
    },
    "jetpack": {
      repo: "projects/addon-sdk",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["projects/addon-sdk", "addon-sdk"]
    },
    "build-system": {
      repo: "projects/build-system",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["build-system", "b-s", "bs"]
    },
    "fx-team": {
      repo: "integration/fx-team",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["fx-team"],
      additions: "[fixed-in-fx-team]"
    },
    "graphics": {
      repo: "projects/graphics",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/graphics", "graphics"]
    },
    "services-central": {
      repo: "services/services-central",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["services-central", "s-c", "sc", "services/services-central"],
      additions: "[fixed in services]"
    },
    "ux": {
      unconditionalFlag: false,
      repo: "projects/ux",
      synonyms: ["ux", "projects/ux"]
    },
    "alder": {
      repo: "projects/alder",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["alder", "projects/alder"]
    },
    "ash": {
      repo: "projects/ash",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["ash", "projects/ash"]
    },
    "birch": {
      repo: "projects/birch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/birch", "birch"]
    },
    "cedar": {
      repo: "projects/cedar",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cedar", "cedar"]
    },
    "cypress": {
      repo: "projects/cypress",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cypress", "cypress"]
    },
    "date": {
      repo: "projects/date",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/date", "date"]
    },
    "elm": {
      repo: "projects/elm",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/elm", "elm"]
    },
    "fig": {
      repo: "projects/fig",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/fig", "fig"],
      additions: "[fixed-in-fig]"
    },
    "gum": {
      repo: "projects/gum",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/gum", "gum"]
    },
    "holly": {
      repo: "projects/holly",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/holly", "holly"]
    },
    "jamun": {
      repo: "projects/jamun",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/jamun", "jamun"]
    },
    "larch": {
      repo: "projects/larch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/larch", "larch"]
    },
    "maple": {
      repo: "projects/maple",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/maple", "maple"]
    },
    "oak": {
      repo: "projects/oak",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/oak", "oak"]
    },
    "pine": {
      repo: "projects/pine",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/pine", "pine"]
    },
    "comm-central": {
      repo: "comm-central",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["comm-central", "c-c"]
    },
    "comm-aurora": {
      repo: "releases/comm-aurora",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-aurora", "comm-aurora"]
    },
    "comm-beta": {
      repo: "releases/comm-beta",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-beta", "comm-beta"]
    },
    "comm-esr31": {
      repo: "releases/comm-esr31",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr31", "comm-esr31"]
    },
    "comm-esr24": {
      repo: "releases/comm-esr24",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr24", "comm-esr24", "comm-release"]
    }
  }
};

Config.bugNumberREs = [Config.bugRE1, Config.bugRE2, Config.bugRE3,
                       Config.bugRE4, Config.bugRE5, Config.bugRE6, Config.bugRE7];

(function () {
  var base = Config.hgBaseURL;
  for (var treeName in Config.treeInfo) {
    var repo = Config.treeInfo[treeName].repo;
    Config.treeInfo[treeName]['hgURL'] = base + repo + '/';
    Config.treeInfo[treeName]['hgRevURL'] = base + repo + '/rev/';
    Config.treeInfo[treeName]['hgPushlogURL'] = base + repo + '/pushloghtml?changeset=';
    Config.treeInfo[treeName]['hgRevRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/', 'ig');
    Config.treeInfo[treeName]['hgRevFullRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/[\\da-f]{12}', 'ig');
    Config.treeInfo[treeName]['hgPushlogRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/pushloghtml\\?changeset=', 'ig');
  }
})();

// Detect input type="email" support
$(document).ready(function() {
  Config.needsValidation = $('#username').prop('type') != 'email';
});
<|endoftext|>


<commit_before>/* jshint strict:false */

var http = require('http');
var url = require('url');
var qs = require('querystring');
var Parameter = require('./parameter');

var defaultConfig = {
  segments: [],
  port: 8000
};

function Metron(config, middleware){
  this.config = config || {};
  this.middleware = middleware || [];
  this.set(config);
}

Metron.prototype.get = function(key){
  return this.config[key];
}

Metron.prototype.set = function(obj){
  for(var key in defaultConfig){
    this.config[key] =
      [obj[key], this.config[key], defaultConfig[key]].filter(function(v){
        return v !== undefined
      })[0];
  }

  return this.config;
}

Metron.prototype.start = function(){
  var server  = http.createServer(this.processRequest.bind(this));
  this.server = server;

  server.listen(this.get('port'));
}

Metron.prototype.stop = function(){
  this.server.close();
}

Metron.prototype.processRequest = function(req, res){
  var params = { };
  var parsedUrl = url.parse(req.url, true);

  if(req.method === 'GET'){
    try{
      req.params = JSON.parse(parsedUrl.query.data);
      this.processParameters(req, res);
    }catch(e){
      return this.endRequest(req, res, 400);
    }
  }else{
    var body = [];

    req.on('data', function (data) {
      body.push(data.toString());
    });

    req.on('end', function () {
      body = body.join('');

      if(req.headers['Content-Type'].toLowerCase().indexOf('json') > -1){
        try{
          params = JSON.parse(body);
        }catch(e){
          return this.endRequest(req, res, 400);
        }
      }else{
        req.params = qs.parse(body);
        this.processParameters(req, res);
      }
    });
  }
}

Metron.prototype.endRequest = function(req, res, statusCode){
  res.writeHead(statusCode || 204);
  res.end();
};

Metron.prototype.processParameters = function(req, res){
  var params = req.params;

  for(var i = 0; i < this.middleware.length; i++){
    this.middleware[i](req, res);
  }

  for(var segmentName in params){
    var segmentConfig = this.config.segments[segmentName];
    var segment = params[segmentName];

    if(!segmentConfig)
      return this.endRequest(req, res, 422);

    for(var statName in segment){
      var statConfig = segmentConfig.stats[statName];
      var statValue = segment[statName];
      statValue = new Parameter(statValue, statConfig).value();

      if(statValue !== undefined){
        var config = { };
        var store = statConfig.dataStore ||
                    segmentConfig.dataStore ||
                    console.log;

        for(var key in segmentConfig){
          if(key == 'stats') continue;
          config[key] = statConfig[key];
        }

        for(key in statConfig){
          config[key] = statConfig[key];
        }

        if(typeof store === 'function'){
          store(statName, statValue, config, req);
        }else if(typeof store === 'object'){
          store.forEach(function(s){
            s(statName, statValue, config, req);
          });
        }
      }
    }
  }

  this.endRequest(req, res);
}

Metron.dataAdapters = {
  Statsd: require('./data/statsd'),
  Log: require('./data/log')
}

module.exports = Metron;
<commit_msg>return early if middleware ended request<commit_after>/* jshint strict:false */

var http = require('http');
var url = require('url');
var qs = require('querystring');
var Parameter = require('./parameter');

var defaultConfig = {
  segments: [],
  port: 8000
};

function Metron(config, middleware){
  this.config = config || {};
  this.middleware = middleware || [];
  this.set(config);
}

Metron.prototype.get = function(key){
  return this.config[key];
}

Metron.prototype.set = function(obj){
  for(var key in defaultConfig){
    this.config[key] =
      [obj[key], this.config[key], defaultConfig[key]].filter(function(v){
        return v !== undefined
      })[0];
  }

  return this.config;
}

Metron.prototype.start = function(){
  var server  = http.createServer(this.processRequest.bind(this));
  this.server = server;

  server.listen(this.get('port'));
}

Metron.prototype.stop = function(){
  this.server.close();
}

Metron.prototype.processRequest = function(req, res){
  var params = { };
  var parsedUrl = url.parse(req.url, true);

  if(req.method === 'GET'){
    try{
      req.params = JSON.parse(parsedUrl.query.data);
      this.processParameters(req, res);
    }catch(e){
      return this.endRequest(req, res, 400);
    }
  }else{
    var body = [];

    req.on('data', function (data) {
      body.push(data.toString());
    });

    req.on('end', function () {
      body = body.join('');

      if(req.headers['Content-Type'].toLowerCase().indexOf('json') > -1){
        try{
          params = JSON.parse(body);
        }catch(e){
          return this.endRequest(req, res, 400);
        }
      }else{
        req.params = qs.parse(body);
        this.processParameters(req, res);
      }
    });
  }
}

Metron.prototype.endRequest = function(req, res, statusCode){
  req.ended = true;
  res.writeHead(statusCode || 204);
  res.end();
};

Metron.prototype.processParameters = function(req, res){
  var params = req.params;

  for(var i = 0; i < this.middleware.length; i++){
    this.middleware[i](req, res);
    // return early if one of the middlewares ended the request.
    if(req.ended) return;
  }

  for(var segmentName in params){
    var segmentConfig = this.config.segments[segmentName];
    var segment = params[segmentName];

    if(!segmentConfig)
      return this.endRequest(req, res, 422);

    for(var statName in segment){
      var statConfig = segmentConfig.stats[statName];
      var statValue = segment[statName];
      statValue = new Parameter(statValue, statConfig).value();

      if(statValue !== undefined){
        var config = { };
        var store = statConfig.dataStore ||
                    segmentConfig.dataStore ||
                    console.log;

        for(var key in segmentConfig){
          if(key == 'stats') continue;
          config[key] = statConfig[key];
        }

        for(key in statConfig){
          config[key] = statConfig[key];
        }

        if(typeof store === 'function'){
          store(statName, statValue, config, req);
        }else if(typeof store === 'object'){
          store.forEach(function(s){
            s(statName, statValue, config, req);
          });
        }
      }
    }
  }

  this.endRequest(req, res);
}

Metron.dataAdapters = {
  Statsd: require('./data/statsd'),
  Log: require('./data/log')
}

module.exports = Metron;
<|endoftext|>


<commit_before>elation.require(['engine.things.camera'], function() {
  elation.component.add('engine.things.camera_admin', function() {
    this.postinit = function() {
      this.defineProperties({
        view: { type: 'object' }
      });
      this.view = this.properties.view;
      console.log('new admin cam!', this.view);

      this.controlstate = this.engine.systems.controls.addContext('camera_admin', {
        'toggle_camera': ['keyboard_ctrl_c', elation.bind(this, function(ev) { if (ev.value == 1) { this.toggleControls(); }}) ]
      });
      this.ears = new THREE.Object3D();
      this.engine.systems.controls.activateContext('camera_admin');
      elation.events.add(this.engine, 'engine_frame', this);
    }
    this.createObject3D = function() {
        this.camera = new THREE.PerspectiveCamera(90, this.view.size[0] / this.view.size[1], 1e-2, 1e4);
        this.camera.position.set(0,1,1);
        this.camera.add(this.ears);
        return this.camera;
    }
    this.createChildren = function() {
        //this.objects['3d'].add(this.camera);

        this.orbitcontrols = new THREE.OrbitControls(this.camera, this.view.container);
        this.orbitcontrols.rotateUp(-Math.PI/4);
        this.orbitcontrols.rotateLeft(-Math.PI/4);
        this.orbitcontrols.dollyOut(10);
        this.orbitcontrols.userPanSpeed = 10;
        this.orbitcontrols.keyPanSpeed = 100;
        this.orbitcontrols.noKeys = true;

        elation.events.add(this.orbitcontrols, 'change', elation.bind(this, this.controls_change));

        this.flycontrols = new THREE.FlyControls(this.camera, this.view.container);
        this.flycontrols.movementSpeed = 10;
        this.flycontrols.rollSpeed = Math.PI/4;
        this.flycontrols.dragToLook = true;

        elation.events.add(this.flycontrols, 'change', elation.bind(this, this.controls_change));

        this.toggleControls();

        this.admincontrols.update(0);
        this.cameraactive = true;

        this.engine.client.setActiveThing(this);
    }
    this.toggleControls = function() {
console.log('toggle controls', this.admincontrols, this.orbitcontrols, this.flycontrols);
      var oldobject = false;
      if (this.admincontrols) {
        if (this.admincontrols.disable) {
          this.admincontrols.disable();
        } else {
          this.admincontrols.enabled = false;
        }
        oldobject = this.admincontrols.object;
      }
      if (this.admincontrols === this.orbitcontrols) {
        this.admincontrols = this.flycontrols;
      } else {
        this.admincontrols = this.orbitcontrols;
      }
      if (this.admincontrols.enable) {
        this.admincontrols.enable()
      } else {
        this.admincontrols.enabled = true;
      }
      if (oldobject) {
        this.admincontrols.object = oldobject;
      }
      this.admincontrols.enabled = true;
    }
    this.enable = function() {
      this.admincontrols.enabled = true;
      this.camera.layers.enable(1);
    }
    this.disable = function() {
      this.admincontrols.enabled = false;
      this.camera.layers.disable(1);
    }
    this.engine_frame = function(ev) {
      if (this.cameraactive) {
        this.admincontrols.update(ev.data.delta);
      }
    }
    this.controls_change = function(ev) {
      this.refresh();
    }
  }, elation.engine.things.camera);
});
<commit_msg>Admin camera control fixes<commit_after>elation.require(['engine.things.camera'], function() {
  elation.component.add('engine.things.camera_admin', function() {
    this.postinit = function() {
      this.defineProperties({
        view: { type: 'object' }
      });
      this.view = this.properties.view;
      console.log('new admin cam!', this.view);

      this.controlstate = this.engine.systems.controls.addContext('camera_admin', {
        'toggle_camera': ['keyboard_ctrl_c', elation.bind(this, function(ev) { if (ev.value == 1) { this.toggleControls(); }}) ]
      });
      this.ears = new THREE.Object3D();
      this.engine.systems.controls.activateContext('camera_admin');
      elation.events.add(this.engine, 'engine_frame', this);
    }
    this.createObject3D = function() {
        this.camera = new THREE.PerspectiveCamera(90, this.view.size[0] / this.view.size[1], 1e-2, 1e4);
        this.camera.position.set(0,1,1);
        this.camera.add(this.ears);
        return this.camera;
    }
    this.createChildren = function() {
        //this.objects['3d'].add(this.camera);

        this.orbitcontrols = new THREE.OrbitControls(this.camera, this.view.container);
        //this.orbitcontrols.rotateUp(-Math.PI/4);
        //this.orbitcontrols.rotateLeft(-Math.PI/4);
        //this.orbitcontrols.dollyOut(10);
        this.orbitcontrols.userPanSpeed = 10;
        this.orbitcontrols.keyPanSpeed = 100;
        this.orbitcontrols.noKeys = true;

        elation.events.add(this.orbitcontrols, 'change', elation.bind(this, this.controls_change));

/*
        this.flycontrols = new THREE.FlyControls(this.camera, this.view.container);
        this.flycontrols.movementSpeed = 10;
        this.flycontrols.rollSpeed = Math.PI/4;
        this.flycontrols.dragToLook = true;
        elation.events.add(this.flycontrols, 'change', elation.bind(this, this.controls_change));
*/

        this.toggleControls();

        this.admincontrols.update(0);
        this.cameraactive = true;

        this.engine.client.setActiveThing(this);
    }
    this.toggleControls = function() {
console.log('toggle controls', this.admincontrols, this.orbitcontrols, this.flycontrols);
      var oldobject = false;
      if (this.admincontrols) {
        if (this.admincontrols.disable) {
          this.admincontrols.disable();
        } else {
          this.admincontrols.enabled = false;
        }
        oldobject = this.admincontrols.object;
      }
      if (this.admincontrols === this.orbitcontrols) {
        this.admincontrols = this.flycontrols;
      } else {
        this.admincontrols = this.orbitcontrols;
      }
      if (this.admincontrols.enable) {
        this.admincontrols.enable()
      } else {
        this.admincontrols.enabled = true;
      }
      if (oldobject) {
        this.admincontrols.object = oldobject;
      }
      this.admincontrols.enabled = true;
    }
    this.enable = function() {
      this.admincontrols.enabled = true;
      this.camera.layers.enable(10);
    }
    this.disable = function() {
      this.admincontrols.enabled = false;
      this.camera.layers.disable(10);
    }
    this.engine_frame = function(ev) {
      if (this.cameraactive) {
        //this.admincontrols.update(ev.data.delta);
        //this.admincontrols.update();
      }
    }
    this.controls_change = function(ev) {
      this.engine.systems.render.setdirty();
    }
  }, elation.engine.things.camera);
});
<|endoftext|>


<commit_before>/**
 * Created by filip on 9.10.14..
 */
'use strict';

angular.module('registryApp.app')
    .controller('WorkflowEditorCtrl', ['$scope', '$rootScope', '$q', '$modal', '$templateCache', 'Loading', 'App', 'User', 'Repo', 'BeforeRedirect', 'Helper', 'PipelineService', 'lodash', 'Globals', 'BeforeUnload', function ($scope, $rootScope, $q, $modal, $templateCache, Loading, App, User, Repo, BeforeRedirect, Helper, PipelineService, _, Globals, BeforeUnload) {
        var PipelineInstance = null;
            //onBeforeUnloadOff = BeforeUnload.register(function() { return 'Please save your changes before leaving.'; });

        $scope.view = {};

        /* workflow mode: new or edit */
        $scope.view.mode = 'edit';

        /* loading state of the page */
        $scope.view.loading = true;

        /* current tab for the right sidebar */
        $scope.view.tab = 'apps';

        /* current workflow */
        $scope.view.workflow = {};

        /* group visibility flags for repos */
        $scope.view.groups = {
            myRepositories: false,
            otherRepositories: false
        };

        /* visibility flags for repo groups that hold apps */
        $scope.view.repoGroups = {};

        $scope.view.repoTypes = {
            MyApps: {},
            PublicApps: {}
        };

        /* list of my repos */
        $scope.view.MyApps = {};

        /* list of other repos */
        $scope.view.PublicApps = {};

        /* list of user repos*/
        $scope.view.userRepos= [];

        /* flag if something is changed: params or workflow */
        $scope.view.isChanged = false;

        /* flag when save is clicked */
        $scope.view.saving = false;

        /* flag for sidebar visibility */
        $scope.view.showSidebar = true;

        $scope.view.classes = ['page', 'workflow-edit'];
        Loading.setClasses($scope.view.classes);

        $scope.Loading = Loading;

        $scope.view.searchTerm = '';

        $scope.view.searchTerm = '';

//        $scope.view.appRevisions = {};

        /**
         * Set controller id for pipeline Service to use it
         *
         * @type {string}
         */
        $scope.view.id = 'workflowEditorCtrl';

        $scope.$watch('Loading.classes', function (n, o) {
            if (n !== o) {
                $scope.view.classes = n;
            }
        });

        PipelineService.register($scope.view.id, function () {
            PipelineInstance = PipelineService.getInstance($scope.view.id);

            console.log('Pipeline Instance cached', PipelineInstance);
        });

        $q.all([
                User.getUser()
//                Repo.getRepos(0, '', true)
            ]).then(function(result) {
                $scope.view.user = result[0].user;
//                $scope.view.userRepos = result[1].list;
            });

        if ($scope.view.mode === 'edit') {
            App.get()
                .then(function (result) {
                    $scope.view.workflow = result.message;
                    $scope.view.loading = false;
                });
        }

        var toggleState = true;

        var toggleAll = function () {

            _.forEach($scope.view.repoTypes.MyApps, function (obj, repo) {
                $scope.view.repoGroups[repo] = toggleState;
            });

            _.forEach($scope.view.repoTypes.PublicApps, function (obj, repo) {
                $scope.view.repoGroups[repo] = toggleState;
            });

            $scope.view.groups.MyApps = toggleState;
            $scope.view.groups.PublicApps = toggleState;

            toggleState = !toggleState;

        };

        $scope.resetSearch = function () {
            $scope.view.searchTerm = '';
            toggleState = false;
            toggleAll();
        };

        $scope.$watch('view.searchTerm', function (newVal,oldVal) {

            if (oldVal !== newVal) {

                if (newVal === '') {
                    $scope.resetSearch();
                } else {
                    toggleState = true;
                    toggleAll();
                }
            }

        });

        /**
         * Callback when apps are loaded
         *
         * @param {Object} result
         */
        var appsLoaded = function (result) {
            $scope.view.filtering = false;
            $scope.view.message = result[0].status;

            $scope.view.repoTypes.MyApps = result[0].message;
            $scope.view.repoTypes.PublicApps = result[1].message;

            $scope.view.loading = false;
        };

//        $scope.toggleAppRevisions = function (rev) {
//            $scope.view.appRevisions[rev].toggled = !$scope.view.appRevisions[rev].toggled;
//        };

        /* load tools/workflows grouped by repositories */
        $q.all([
            App.getMineAppsByProject(),
            App.getPublicAppsByProject()
        ]).then(appsLoaded);

        /**
         * Switch tab on the right side
         *
         * @param {string} tab
         */
        $scope.switchTab = function (tab) {
            $scope.view.tab = tab;
        };

        /**
         * Toggle top level groups
         * @param group
         */
        $scope.toggleGroup = function (group) {
            $scope.view.groups[group] = !$scope.view.groups[group];
        };

        /**
         * Toggle repo list visibility
         *
         * @param repo
         */
        $scope.toggleRepo = function (repo) {
            if (_.isUndefined($scope.view.repoGroups[repo])) {
                $scope.view.repoGroups[repo] = false;
            }
            $scope.view.repoGroups[repo] = !$scope.view.repoGroups[repo];
        };

        /**
         * Callback when workflow is changed
         */
        $scope.onWorkflowChange = function (value) {

            $scope.view.isChanged = value;

            if (!value) {
                $scope.view.saving = false;
                $scope.view.loading = false;

                $scope.view.currentAppId = null;
                $scope.view.json = {};
            }

        };

        /**
         * Initiate workflow save
         */
        $scope.save = function () {
            BeforeRedirect.setReload(true);
            $scope.view.saving = true;
            $scope.view.loading = true;

            var workflow = PipelineInstance.format();
            App.update(workflow, 'workflow').then(function(data) {

                var rev = data.message['sbg:revision'];
                var url, arr = window.location.href.split('/');

                if (arr[arr.length-1] === '') {
                    arr.pop()
                }

                arr.pop();
                arr.push(rev.toString());

                url = arr.join('/');

                $scope.view.loading = false;

                var modalInstance = $modal.open({
                    template: $templateCache.get('views/cliche/partials/app-save-response.html'),
                    controller: 'ModalCtrl',
                    backdrop: 'static',
                    resolve: { data: function () { return { trace: {message: 'Workflow successfully saved.'} }; }}
                });

                modalInstance.result.then(function() {
                    window.location.href = url;
                });

            });
        };

        $scope.toggleSidebar = function() {

            $scope.view.showSidebar = !$scope.view.showSidebar;
//            $rootScope.$broadcast('sidebar:toggle', $scope.view.showSidebar);
            PipelineInstance.adjustSize();

        };

        /**
         * Check if particular property is not exposed anymore and remove it from values schema list
         *
         * @param {string} appName
         * @param {string} key
         */
        $scope.onExpose = function (appName, key) {

            if (!_.isUndefined($scope.view.values[appName]) && !_.isUndefined($scope.view.values[appName][key])) {
                delete $scope.view.values[appName][key];
            }

            if (!_.isUndefined($scope.view.values[appName]) && _.isEmpty($scope.view.values[appName])) {
                delete $scope.view.values[appName];
            }

            console.log($scope.view.exposed);
            console.log($scope.view.values);

        };

        /**
         * Track node select
         */
        var onNodeSelect = function (e, model, exposed, values) {

            $scope.view.json = model;

            $scope.view.values = values;
            $scope.view.exposed = exposed;

            $scope.view.required = $scope.view.json.inputs.required;

            $scope.switchTab('params');
            $scope.$digest();
        };

        /**
         * Track node deselect
         */
        var onNodeDeselect = function () {

            $scope.view.json = {};

            $scope.switchTab('apps');
            $scope.$digest();
        };

        var onNodeSelectOff = $rootScope.$on('node:select', onNodeSelect);
        var onNodeDeselectOff = $rootScope.$on('node:deselect', onNodeDeselect);

        var onBeforeRedirectOff = BeforeRedirect.register(function () {

            var deferred = $q.defer();

            if ($scope.view.mode === 'new') {
                $scope.$broadcast('save-local', true);
            }

            deferred.resolve();

            return deferred.promise;

        });

        /**
         * Toggle dropdown menu
         */
        $scope.toggleMenu = function() {

            $scope.view.isMenuOpen = !$scope.view.isMenuOpen;

        };

        $scope.view.capitalize = function (string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        };

        /**
         * Load markdown modal for description edit
         */
        $scope.loadMarkdown = function() {

            var modalInstance = $modal.open({
                template: $templateCache.get('views/partials/markdown.html'),
                controller: 'MarkdownCtrl',
                windowClass: 'modal-markdown',
                size: 'lg',
                backdrop: 'static',
                resolve: {data: function () {return {markdown: $scope.view.workflow.description};}}
            });

            modalInstance.result.then(function(result) {
                $scope.view.workflow.description = result;
            });
        };

        $scope.editMetadata = function () {

            var modalInstance = $modal.open({
                template: $templateCache.get('views/dyole/edit-metadata.html'),
                controller: 'DyoleEditMetadataCtrl',
                windowClass: 'modal-markdown',
                size: 'lg',
                backdrop: 'static',
                resolve: {data: function () {return {tool: $scope.view.workflow};}}
            });

            modalInstance.result.then(function(result) {
                $scope.view.workflow.description = result;
            });

        };

        $scope.runWorkflow = function () {
            // create task and redirect to task page for that task
        };

        $scope.validateWorkflowJSON = function () {

            PipelineInstance.validate().then(function (workflow) {
                $modal.open({
                    template: $templateCache.get('views/dyole/json-modal.html'),
                    controller: 'ModalJSONCtrl',
                    resolve: {data: function () {
                        return {json: workflow.errors, url: false};
                    }}
                });
            });

        };

        $scope.workflowToJSON = function () {
            var workflow = PipelineInstance.format();

            var modal = $modal.open({
                template: $templateCache.get('views/dyole/json-modal.html'),
                controller: 'ModalJSONCtrl',
                resolve: {data: function () {
                    return {json: workflow};
                }}
            });

            modal.result.then(function () {
                PipelineInstance.getUrl({url: App.getAppUrl()});
            });
        };

        $scope.$on('$destroy', function () {
            onNodeSelectOff();
            onNodeDeselectOff();

            onBeforeRedirectOff();
            onBeforeRedirectOff = undefined;

            onBeforeUnloadOff();
            onBeforeUnloadOff = undefined;

            PipelineService.removeInstance($scope.view.id);

        });

    }]);
<commit_msg>Dirty checking for beforeunload prompt for Dyole.<commit_after>/**
 * Created by filip on 9.10.14..
 */
'use strict';

angular.module('registryApp.app')
    .controller('WorkflowEditorCtrl', ['$scope', '$rootScope', '$q', '$modal', '$templateCache', 'Loading', 'App', 'User', 'Repo', 'BeforeRedirect', 'Helper', 'PipelineService', 'lodash', 'Globals', 'BeforeUnload', function ($scope, $rootScope, $q, $modal, $templateCache, Loading, App, User, Repo, BeforeRedirect, Helper, PipelineService, _, Globals, BeforeUnload) {
        var PipelineInstance = null,
            prompt = false,
            onBeforeUnloadOff = BeforeUnload.register(function() { return 'Please save your changes before leaving.'; }, function() {return prompt});

        $scope.$on('pipeline:change', function() {
            prompt = true;
        });

        $scope.view = {};

        /* workflow mode: new or edit */
        $scope.view.mode = 'edit';

        /* loading state of the page */
        $scope.view.loading = true;

        /* current tab for the right sidebar */
        $scope.view.tab = 'apps';

        /* current workflow */
        $scope.view.workflow = {};

        /* group visibility flags for repos */
        $scope.view.groups = {
            myRepositories: false,
            otherRepositories: false
        };

        /* visibility flags for repo groups that hold apps */
        $scope.view.repoGroups = {};

        $scope.view.repoTypes = {
            MyApps: {},
            PublicApps: {}
        };

        /* list of my repos */
        $scope.view.MyApps = {};

        /* list of other repos */
        $scope.view.PublicApps = {};

        /* list of user repos*/
        $scope.view.userRepos= [];

        /* flag if something is changed: params or workflow */
        $scope.view.isChanged = false;

        /* flag when save is clicked */
        $scope.view.saving = false;

        /* flag for sidebar visibility */
        $scope.view.showSidebar = true;

        $scope.view.classes = ['page', 'workflow-edit'];
        Loading.setClasses($scope.view.classes);

        $scope.Loading = Loading;

        $scope.view.searchTerm = '';

        $scope.view.searchTerm = '';

//        $scope.view.appRevisions = {};

        /**
         * Set controller id for pipeline Service to use it
         *
         * @type {string}
         */
        $scope.view.id = 'workflowEditorCtrl';

        $scope.$watch('Loading.classes', function (n, o) {
            if (n !== o) {
                $scope.view.classes = n;
            }
        });

        PipelineService.register($scope.view.id, function () {
            PipelineInstance = PipelineService.getInstance($scope.view.id);

            console.log('Pipeline Instance cached', PipelineInstance);
        });

        $q.all([
                User.getUser()
//                Repo.getRepos(0, '', true)
            ]).then(function(result) {
                $scope.view.user = result[0].user;
//                $scope.view.userRepos = result[1].list;
            });

        if ($scope.view.mode === 'edit') {
            App.get()
                .then(function (result) {
                    $scope.view.workflow = result.message;
                    $scope.view.loading = false;
                });
        }

        var toggleState = true;

        var toggleAll = function () {

            _.forEach($scope.view.repoTypes.MyApps, function (obj, repo) {
                $scope.view.repoGroups[repo] = toggleState;
            });

            _.forEach($scope.view.repoTypes.PublicApps, function (obj, repo) {
                $scope.view.repoGroups[repo] = toggleState;
            });

            $scope.view.groups.MyApps = toggleState;
            $scope.view.groups.PublicApps = toggleState;

            toggleState = !toggleState;

        };

        $scope.resetSearch = function () {
            $scope.view.searchTerm = '';
            toggleState = false;
            toggleAll();
        };

        $scope.$watch('view.searchTerm', function (newVal,oldVal) {

            if (oldVal !== newVal) {

                if (newVal === '') {
                    $scope.resetSearch();
                } else {
                    toggleState = true;
                    toggleAll();
                }
            }

        });

        /**
         * Callback when apps are loaded
         *
         * @param {Object} result
         */
        var appsLoaded = function (result) {
            $scope.view.filtering = false;
            $scope.view.message = result[0].status;

            $scope.view.repoTypes.MyApps = result[0].message;
            $scope.view.repoTypes.PublicApps = result[1].message;

            $scope.view.loading = false;
        };

//        $scope.toggleAppRevisions = function (rev) {
//            $scope.view.appRevisions[rev].toggled = !$scope.view.appRevisions[rev].toggled;
//        };

        /* load tools/workflows grouped by repositories */
        $q.all([
            App.getMineAppsByProject(),
            App.getPublicAppsByProject()
        ]).then(appsLoaded);

        /**
         * Switch tab on the right side
         *
         * @param {string} tab
         */
        $scope.switchTab = function (tab) {
            $scope.view.tab = tab;
        };

        /**
         * Toggle top level groups
         * @param group
         */
        $scope.toggleGroup = function (group) {
            $scope.view.groups[group] = !$scope.view.groups[group];
        };

        /**
         * Toggle repo list visibility
         *
         * @param repo
         */
        $scope.toggleRepo = function (repo) {
            if (_.isUndefined($scope.view.repoGroups[repo])) {
                $scope.view.repoGroups[repo] = false;
            }
            $scope.view.repoGroups[repo] = !$scope.view.repoGroups[repo];
        };

        /**
         * Callback when workflow is changed
         */
        $scope.onWorkflowChange = function (value) {

            $scope.view.isChanged = value;

            if (!value) {
                $scope.view.saving = false;
                $scope.view.loading = false;

                $scope.view.currentAppId = null;
                $scope.view.json = {};
            }

        };

        /**
         * Initiate workflow save
         */
        $scope.save = function () {
            BeforeRedirect.setReload(true);
            $scope.view.saving = true;
            $scope.view.loading = true;

            var workflow = PipelineInstance.format();
            App.update(workflow, 'workflow').then(function(data) {

                var rev = data.message['sbg:revision'];
                var url, arr = window.location.href.split('/');

                if (arr[arr.length-1] === '') {
                    arr.pop()
                }

                arr.pop();
                arr.push(rev.toString());

                url = arr.join('/');

                $scope.view.loading = false;

                var modalInstance = $modal.open({
                    template: $templateCache.get('views/cliche/partials/app-save-response.html'),
                    controller: 'ModalCtrl',
                    backdrop: 'static',
                    resolve: { data: function () { return { trace: {message: 'Workflow successfully saved.'} }; }}
                });

                modalInstance.result.then(function() {
                    window.location.href = url;
                });

            });
        };

        $scope.toggleSidebar = function() {

            $scope.view.showSidebar = !$scope.view.showSidebar;
//            $rootScope.$broadcast('sidebar:toggle', $scope.view.showSidebar);
            PipelineInstance.adjustSize();

        };

        /**
         * Check if particular property is not exposed anymore and remove it from values schema list
         *
         * @param {string} appName
         * @param {string} key
         */
        $scope.onExpose = function (appName, key) {

            if (!_.isUndefined($scope.view.values[appName]) && !_.isUndefined($scope.view.values[appName][key])) {
                delete $scope.view.values[appName][key];
            }

            if (!_.isUndefined($scope.view.values[appName]) && _.isEmpty($scope.view.values[appName])) {
                delete $scope.view.values[appName];
            }

            console.log($scope.view.exposed);
            console.log($scope.view.values);

        };

        /**
         * Track node select
         */
        var onNodeSelect = function (e, model, exposed, values) {

            $scope.view.json = model;

            $scope.view.values = values;
            $scope.view.exposed = exposed;

            $scope.view.required = $scope.view.json.inputs.required;

            $scope.switchTab('params');
            $scope.$digest();
        };

        /**
         * Track node deselect
         */
        var onNodeDeselect = function () {

            $scope.view.json = {};

            $scope.switchTab('apps');
            $scope.$digest();
        };

        var onNodeSelectOff = $rootScope.$on('node:select', onNodeSelect);
        var onNodeDeselectOff = $rootScope.$on('node:deselect', onNodeDeselect);

        var onBeforeRedirectOff = BeforeRedirect.register(function () {

            var deferred = $q.defer();

            if ($scope.view.mode === 'new') {
                $scope.$broadcast('save-local', true);
            }

            deferred.resolve();

            return deferred.promise;

        });

        /**
         * Toggle dropdown menu
         */
        $scope.toggleMenu = function() {

            $scope.view.isMenuOpen = !$scope.view.isMenuOpen;

        };

        $scope.view.capitalize = function (string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        };

        /**
         * Load markdown modal for description edit
         */
        $scope.loadMarkdown = function() {

            var modalInstance = $modal.open({
                template: $templateCache.get('views/partials/markdown.html'),
                controller: 'MarkdownCtrl',
                windowClass: 'modal-markdown',
                size: 'lg',
                backdrop: 'static',
                resolve: {data: function () {return {markdown: $scope.view.workflow.description};}}
            });

            modalInstance.result.then(function(result) {
                $scope.view.workflow.description = result;
            });
        };

        $scope.editMetadata = function () {

            var modalInstance = $modal.open({
                template: $templateCache.get('views/dyole/edit-metadata.html'),
                controller: 'DyoleEditMetadataCtrl',
                windowClass: 'modal-markdown',
                size: 'lg',
                backdrop: 'static',
                resolve: {data: function () {return {tool: $scope.view.workflow};}}
            });

            modalInstance.result.then(function(result) {
                $scope.view.workflow.description = result;
            });

        };

        $scope.runWorkflow = function () {
            // create task and redirect to task page for that task
        };

        $scope.validateWorkflowJSON = function () {

            PipelineInstance.validate().then(function (workflow) {
                $modal.open({
                    template: $templateCache.get('views/dyole/json-modal.html'),
                    controller: 'ModalJSONCtrl',
                    resolve: {data: function () {
                        return {json: workflow.errors, url: false};
                    }}
                });
            });

        };

        $scope.workflowToJSON = function () {
            var workflow = PipelineInstance.format();

            var modal = $modal.open({
                template: $templateCache.get('views/dyole/json-modal.html'),
                controller: 'ModalJSONCtrl',
                resolve: {data: function () {
                    return {json: workflow};
                }}
            });

            modal.result.then(function () {
                PipelineInstance.getUrl({url: App.getAppUrl()});
            });
        };

        $scope.$on('$destroy', function () {
            onNodeSelectOff();
            onNodeDeselectOff();

            onBeforeRedirectOff();
            onBeforeRedirectOff = undefined;

            onBeforeUnloadOff();
            onBeforeUnloadOff = undefined;

            PipelineService.removeInstance($scope.view.id);

        });

    }]);
<|endoftext|>


<commit_before>
var Config = {

    // API
    nearbyRacksURL: 'static/data/austin_racks_v1.json',

    rackIconOptions: {
        iconUrl: 'static/images/parking_bicycle_0.png',
        shadowUrl: 'static/images/parking_bicycle_shadow_0.png',
        clusterIconUrl: 'static/images/parking_bicycle_cluster_0.png',
        clusterShadowUrl: 'static/images/parking_bicycle_cluster_shadow_0.png',
        iconAnchor: [13, 32],
        popupAnchor: [5, -24]
    },

    markerClusterOptions: {
        maxClusterRadius: 30
    },

    geoOptions: {
        enableHighAccuracy: true
    },

    // Map view config
    defaultZoom: 14

};
<commit_msg>Add bikeracks and Imgur API params to config<commit_after>var Config = (function() {

    var rootURL = 'http://john.bitsurge.net/bikeracks';
    var imageURL = '/static/images';
    return {

        // API
        nearbyRacksURL: '/static/data/austin_racks_v1.json',
        updateRackURL: rootURL + '/cgi-bin/bikeracks.py',
        getRackURL: rootURL + '/get/',

        // Imgur API
        imgurClientID: 'f962410f5a6a13d',

        rackIconOptions: {
            iconUrl: imageURL + '/parking_bicycle_0.png',
            shadowUrl: imageURL + '/parking_bicycle_shadow_0.png',
            clusterIconUrl: imageURL + '/parking_bicycle_cluster_0.png',
            clusterShadowUrl:
                imageURL + '/parking_bicycle_cluster_shadow_0.png',
            iconAnchor: [13, 32],
            popupAnchor: [5, -24]
        },

        markerClusterOptions: {
            maxClusterRadius: 30
        },

        geoOptions: {
            enableHighAccuracy: true
        },

        // Map view config
        defaultZoom: 14

    };
})();
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import {fetchProducts} from '../actions/index';
import Product from '../components/product';
import _ from 'lodash';

class Products extends Component {
  componentDidMount() {
    const {id} = this.props.match.params;
    this.props.fetchProducts(id);
  }

  getHoverImages(product) {
    return _.map(product.product_images, image => {
      console.log(image.index);
      return (

      );
    });
  }

  playImages() {
    console.log("TEst");
  }

  stopAndBackToBegin() {
    console.log("TEst");
  }

  showProducts() {
    return _.map(this.props.products, product => {
      return (

          <Link to={`productos/${product.id}`}>

                {this.getHoverImages(product)}

{product.name}

{product.description}

{product.available ? 'Disponible' : 'No Disponible'}

          </Link>

      );
    });
  }

  render() {
    return(

        {this.showProducts()}

    );
  }
}

function mapStateToProps(state) {
  return {products: state.products}
}

export default connect(mapStateToProps, {fetchProducts})(Products);
<commit_msg>workig in hover<commit_after>import React, {Component} from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import {fetchProducts} from '../actions/index';
import Product from '../components/product';
import _ from 'lodash';

class Products extends Component {
  constructor(props) {
    super(props);
    this.playImages =  this.playImages.bind(this);
  }

  componentDidMount() {
    const {id} = this.props.match.params;
    this.props.fetchProducts(id);
  }

  getHoverImages(product) {
    return _.map(product.product_images, image => {
      console.log(image.index);
      return (

      );
    });
  }

  playImages(event) {
    const divHolder =  event.target;
    const images = divHolder.querySelectorAll('img');

    _.forEach(images, (value, key) => {
      console.log(value);
    });

  }

  stopAndBackToBegin() {
    console.log("TEst");
  }

  showProducts() {
    return _.map(this.props.products, product => {
      return (

          <Link to={`productos/${product.id}`}>

                {this.getHoverImages(product)}

{product.name}

{product.description}

{product.available ? 'Disponible' : 'No Disponible'}

          </Link>

      );
    });
  }

  render() {
    return(

        {this.showProducts()}

    );
  }
}

function mapStateToProps(state) {
  return {products: state.products}
}

export default connect(mapStateToProps, {fetchProducts})(Products);
<|endoftext|>