<commit_before>/*global define*/
/*jslint white:true*/
/**
 * Loads the required narrative configuration files.
 * This returns a Promise that will eventually hold the results.
 * This should mainly be invoked by the starting app, then
 * that result should be injected where necessary.
 *
 * @author Bill Riehl <wjriehl@lbl.gov>
 * @class narrativeConfig
 * @module Narrative
 * @static
 */
define([
    'jquery',
    'bluebird',
    'json!kbase/config/config.json',
    'json!kbase/config/icons.json'],
function($,
         Promise,
         configSet,
         iconsSet) {
    'use strict';

    // Get the workspace id from the URL
    var workspaceId = null;
    var m = window.location.href.match(/ws\.(\d+)\.obj\.(\d+)/);
    if (m && m.length > 1) {
        workspaceId = parseInt(m[1]);
    }

    // Build the config up from the configSet (from config.json)
    var config = {
        environment:       configSet.config,
        urls:              configSet[configSet.config],
        version:           configSet.version,
        name:              configSet.name,
        git_commit_hash:   configSet.git_commit_hash,
        git_commit_time:   configSet.git_commit_time,
        release_notes:     configSet.release_notes,
        mode:              configSet.mode,
        dev_mode:          configSet.dev_mode,
        tooltip:           configSet.tooltip,
        icons:             iconsSet,
        workspaceId:       workspaceId,
        loading_gif:       configSet.loading_gif,
        use_local_widgets: configSet.use_local_widgets
    };

    var debug = config.mode === "debug";
    config.debug = debug;

    // Add a remote UI-common to the Require.js config
    require.config({
        paths: {
            uiCommonPaths: config.urls.ui_common_root + 'widget-paths'
        }
    });
    window.kbconfig = config;

    /**
     * Updates the RequireJS config with additional locations from
     * a config given by the ui-common repo. This file is expected to be
     * called "widget-paths.js" and should be deployed in the configured
     * ui-common location.
     *
     * Note that this is optional. If we're expected to use local widgets,
     * as configured with the use_local_widgets flag, then skip this step
     * and just run the callback.
     */
    function updateConfig() {
        return Promise.try(function() {
            console.log('Config: checking remote widgets');
            if (!config.use_local_widgets) {
                // var uiCommonPaths = config.urls.ui_common_root + "widget-paths.json";
                require(['uiCommonPaths'], function(pathConfig) {
                    for (var name in pathConfig.paths) {
                        pathConfig.paths[name] = config.urls.ui_common_root + pathConfig.paths[name];
                    }
                    require.config(pathConfig);
                    config.new_paths = pathConfig;
                    if (callback) {
                        callback(config);
                    }
                }, function(error) {
                    console.log("Unable to get updated widget paths. Sticking with what we've got.");
                });
            }
            return config;
        })
        .then(function(config) {
            console.log('Config: fetching remote data configuration.');
            return Promise.resolve($.getJSON(config.urls.data_panel_sources));
        })
        .then(function(dataCategories) {
            console.log('Config: processing remote data configuration.');
            config.publicCategories = dataCategories[config.environment].publicData;
            config.exampleData = dataCategories[config.environment].exampleData;
            return Promise.try(function() { return config; });
        })
        .catch(function(error) {
            console.error('Config: unable to process remote data configuration options. Searching locally.');
            // hate embedding this stuff, but it seems the only good way.
            // the filename is the last step of that url path (after the last /)
            var path = config.urls.data_panel_sources.split('/');

            return Promise.resolve($.getJSON('static/kbase/config/' + path[path.length-1]))
            .then(function(dataCategories) {
                console.log('Config: processing local data configuration.');
                config.publicCategories = dataCategories[config.environment].publicData;
                config.exampleData = dataCategories[config.environment].exampleData;
                return Promise.try(function() { return config; });
            })
            .catch(function(error) {
                console.error('Config: unable to process local configuration options, too! Public and Example data unavailable!');
                return Promise.try(function() { return config; });
            });
        })
    }

    /**
     * Simple wrapper to return a URL by its key. If not present, just returns undefined.
     */
    function url(key) {
        return config.urls[key];
    }

    /**
     * Simple wrapper to return some value by its key. If not present, just returns undefined.
     */
    function get(key) {
        return config[key];
    }

    return {
        updateConfig: updateConfig,
        config: config,
        url: url,
        get: get,
        debug: debug
    }
});<commit_msg>move config loading code into a loadConfig function. It returns a promise, which must be fulfilled before the config is complete. Previously was implicitly constructed during module load.<commit_after>/*global define*/
/*jslint white:true*/
/**
 * Loads the required narrative configuration files.
 * This returns a Promise that will eventually hold the results.
 * This should mainly be invoked by the starting app, then
 * that result should be injected where necessary.
 *
 * @author Bill Riehl <wjriehl@lbl.gov>
 * @class narrativeConfig
 * @module Narrative
 * @static
 */
define([
    'jquery',
    'bluebird',
    'json!kbase/config/config.json',
    'json!kbase/config/icons.json',
    'require'],
    function ($,
        Promise,
        configSet,
        iconsSet,
        localRequire) {
        'use strict';

        var config, debug;

        function loadConfig() {
            return Promise.try(function () {
                if (window.kbconfig) {
                    return window.kbconfig;
                }
                // Get the workspace id from the URL
                var workspaceId = null;
                var m = window.location.href.match(/ws\.(\d+)\.obj\.(\d+)/);
                if (m && m.length > 1) {
                    workspaceId = parseInt(m[1]);
                }

                // Build the config up from the configSet (from config.json)
                config = {
                    environment: configSet.config,
                    urls: configSet[configSet.config],
                    version: configSet.version,
                    name: configSet.name,
                    git_commit_hash: configSet.git_commit_hash,
                    git_commit_time: configSet.git_commit_time,
                    release_notes: configSet.release_notes,
                    mode: configSet.mode,
                    dev_mode: configSet.dev_mode,
                    tooltip: configSet.tooltip,
                    icons: iconsSet,
                    workspaceId: workspaceId,
                    loading_gif: configSet.loading_gif,
                    use_local_widgets: configSet.use_local_widgets
                };

                debug = config.mode === "debug";
                config.debug = debug;

                // Add a remote UI-common to the Require.js config
                require.config({
                    paths: {
                        uiCommonPaths: config.urls.ui_common_root + 'widget-paths'
                    }
                });
                window.kbconfig = config;

                return new Promise(function (resolve, reject) {
                    var servicesConfigPath = 'kbase/config/services-' + configSet.config + '.json';
                    localRequire([
                        'json!' + servicesConfigPath
                    ], function (servicesConfig) {
                        try {
                            // Merge service config into the main config object.
                            Object.keys(servicesConfig).forEach(function (key) {
                                config[key] = servicesConfig[key];
                            });
                            resolve(config);
                        } catch (ex) {
                            reject(ex);
                        }
                    }, function (err) {
                        reject(err);
                    });
                });
            });
        }

        function assertConfig() {
            if (config === undefined) {
                throw new Error('Config has not yet been loaded');
            }
        }

        /**
         * Updates the RequireJS config with additional locations from
         * a config given by the ui-common repo. This file is expected to be
         * called "widget-paths.js" and should be deployed in the configured
         * ui-common location.
         *
         * Note that this is optional. If we're expected to use local widgets,
         * as configured with the use_local_widgets flag, then skip this step
         * and just run the callback.
         */
        function updateConfig() {
            return new Promise.try(function (resolve, reject) {
                if (window.kbconfig) {
                    return window.kbconfig;
                }
                console.log('Config: checking remote widgets');
                assertConfig();
                if (!config.use_local_widgets) {
                    // var uiCommonPaths = config.urls.ui_common_root + "widget-paths.json";
                    require(['uiCommonPaths'], function (pathConfig) {
                        for (var name in pathConfig.paths) {
                            pathConfig.paths[name] = config.urls.ui_common_root + pathConfig.paths[name];
                        }
                        require.config(pathConfig);
                        config.new_paths = pathConfig;
                        resolve(config);
                    }, function () {
                        console.warn("Unable to get updated widget paths. Sticking with what we've got.");
                        resolve(config);
                    });
                } else {
                    resolve(config);
                }
            })
                .then(function (config) {
                    console.log('Config: fetching remote data configuration.');
                    return Promise.resolve($.getJSON(config.urls.data_panel_sources));
                })
                .then(function (dataCategories) {
                    console.log('Config: processing remote data configuration.');
                    config.publicCategories = dataCategories[config.environment].publicData;
                    config.exampleData = dataCategories[config.environment].exampleData;
                    return Promise.try(function () {
                        return config;
                    });
                })
                .catch(function (error) {
                    console.error('Config: unable to process remote data configuration options. Searching locally.');
                    // hate embedding this stuff, but it seems the only good way.
                    // the filename is the last step of that url path (after the last /)
                    var path = config.urls.data_panel_sources.split('/');

                    return Promise.resolve($.getJSON('static/kbase/config/' + path[path.length - 1]))
                        .then(function (dataCategories) {
                            console.log('Config: processing local data configuration.');
                            config.publicCategories = dataCategories[config.environment].publicData;
                            config.exampleData = dataCategories[config.environment].exampleData;
                            return Promise.try(function () {
                                return config;
                            });
                        })
                        .catch(function (error) {
                            console.error('Config: unable to process local configuration options, too! Public and Example data unavailable!');
                            return Promise.try(function () {
                                return config;
                            });
                        });
                });
        }

        /**
         * Simple wrapper to return a URL by its key. If not present, just returns undefined.
         */
        function url(key) {
            assertConfig();
            return config.urls[key];
        }

        /**
         * Simple wrapper to return some value by its key. If not present, just returns undefined.
         */
        function get(key) {
            assertConfig();
            return config[key];
        }

        /*
         * If the module is defined in multiple module loaders, the module variable config and debug will
         * not be available.
         */
        function getConfig() {
            return window.kbconfig;
        }

        return {
            updateConfig: updateConfig,
            loadConfig: loadConfig,
            config: config,
            getConfig: getConfig,
            url: url,
            get: get,
            debug: debug
        }
    });<|endoftext|>


<commit_before>/**
 * @module Lyria
 * @submodule Scene
 */
define('lyria/scene/director', ['root', 'mixer', 'jquery', 'eventmap', 'lyria/scene', 'lyria/viewport'], function(root, mixer, $, EventMap, Scene, Viewport) {'use strict';

  /**
   * The scene director is the manager for all scenes
   *
   */
  return (function() {

    /**
     * The scene director constructor
     * Attaches a scene director to a container, the parent is optional
     *
     * @class Director
     * @constructor
     *
     * @param {Object} container
     * @param {Object} parent
     */
    function SceneDirector(container, parent) {
      mixer(SceneDirector.prototype, new EventMap());

      if ( container instanceof Viewport) {
        this.viewport = container;
      } else {
        this.viewport = new Viewport(container, {parent: parent});
      }

      /**
       * All scenes
       *
       * @property sceneList
       * @type {Object}
       */
      this.sceneList = {};

      /**
       * The current scene
       *
       * @property currentScene
       * @type {Scene}
       */
      this.currentScene = null;

      /**
       * The default scene
       *
       * @property defaultScene
       * @type {String}
       */
      this.defaultScene = null;

      /**
       * Define events for scene director
       *
       */
      this.on('render', function() {
        if (this.currentScene) {
          this.currentScene.trigger('render');
        }
      });

      this.on('update', function(dt) {
        if (this.currentScene) {
          this.currentScene.trigger('update', dt);
        }
      });
    }

    // Properties
    SceneDirector.prototype.sceneClassName = 'scene';

    // Methods

    /**
     * Adds a scene to the scene director
     *
     * @method add
     * @param {Object} scene
     * @param {Object} options
     */
    SceneDirector.prototype.add = function(scene, options) {

      // Shorthand to add all scenes to the scene director
      if (scene === '*' && this.scenes) {
        scene = Object.keys(this.scenes);
      }

      // Allow array as scenes
      if (Array.isArray(scene)) {
        for (var i = 0, j = scene.length; i < j; i++) {
          this.add(scene[i], options);
        }

        return;
      }

      if (!( scene instanceof Scene)) {
        if (this.scenes && !$.isEmptyObject(this.scenes)) {
          scene = this.scenes[scene];
        } else {
          // TODO: Don't throw a string. Throw an error
          throw 'No valid scene found.';
        }
      }

      scene.parent = this;

      // Update reference to the game itself
      if (this.parent != null) {
        scene.game = this.parent;
      }

      // Add first scene as a default scene
      if (Object.keys(sceneList).length === 0) {
        this.defaultScene = scene;
      }

      this.sceneList[scene.name] = scene;

      if (this.viewport.$element) {
        if ($('#' + scene.name).length === 0) {
          this.viewport.$element.prepend($(root.document.createElement('div')).attr('id', scene.name).attr('class', SceneDirector.prototype.sceneClassName));

          if (!scene.isAsync) {
            scene.trigger('added');
          }

        }
      }

      return this;
    };

    /**
     * Shows a specified scene
     *
     * @method show
     * @param {String} scene name of scene
     * @param {Object} options
     * @param {Function} callback
     */
    SceneDirector.prototype.show = function(sceneName, options, callback) {
      // More than one scene visible at the same time
      if ($('.' + SceneDirector.prototype.sceneClassName + ':visible')) {
        $('.' + SceneDirector.prototype.sceneClassName).hide();
      }

      if (this.currentScene) {
        if (this.currentScene.transition && this.currentScene.length) {
          $('#' + this.currentScene).hide(this.currentScene.transition.length, function() {
            $('.' + SceneDirector.prototype.sceneClassName).hide();
          });
        } else {
          $('.' + SceneDirector.prototype.sceneClassName).hide();
        }

        this.currentScene.trigger('deactivate', options);
      }

      var self = this;

      self.currentScene = this.sceneList[sceneName];

      if (self.currentScene.transition && self.currentScene.transition.length) {
        $('#' + sceneName).show(self.currentScene.transition.length);
      } else {
        $('#' + sceneName).show();
      }
      this.trigger('scene:change', sceneName);
      self.currentScene.trigger('active', options);

      if (callback) {
        callback(sceneName);
      }
    };

    /**
     * Refreshes a scene
     *
     * @method refresh
     * @param {String} scene
     */
    SceneDirector.prototype.refresh = function(scene) {
      var sceneObj = (scene) ? this.sceneList[scene] : this.currentScene;

      // Re-compile scene template
      sceneObj.refresh();
    };

    return SceneDirector;

  })();
});
<commit_msg>Fixes reference<commit_after>/**
 * @module Lyria
 * @submodule Scene
 */
define('lyria/scene/director', ['root', 'mixer', 'jquery', 'eventmap', 'lyria/scene', 'lyria/viewport'], function(root, mixer, $, EventMap, Scene, Viewport) {'use strict';

  /**
   * The scene director is the manager for all scenes
   *
   */
  return (function() {

    /**
     * The scene director constructor
     * Attaches a scene director to a container, the parent is optional
     *
     * @class Director
     * @constructor
     *
     * @param {Object} container
     * @param {Object} parent
     */
    function SceneDirector(container, parent) {
      mixer(SceneDirector.prototype, new EventMap());

      if ( container instanceof Viewport) {
        this.viewport = container;
      } else {
        this.viewport = new Viewport(container, {parent: parent});
      }

      /**
       * All scenes
       *
       * @property sceneList
       * @type {Object}
       */
      this.sceneList = {};

      /**
       * The current scene
       *
       * @property currentScene
       * @type {Scene}
       */
      this.currentScene = null;

      /**
       * The default scene
       *
       * @property defaultScene
       * @type {String}
       */
      this.defaultScene = null;

      /**
       * Define events for scene director
       *
       */
      this.on('render', function() {
        if (this.currentScene) {
          this.currentScene.trigger('render');
        }
      });

      this.on('update', function(dt) {
        if (this.currentScene) {
          this.currentScene.trigger('update', dt);
        }
      });
    }

    // Properties
    SceneDirector.prototype.sceneClassName = 'scene';

    // Methods

    /**
     * Adds a scene to the scene director
     *
     * @method add
     * @param {Object} scene
     * @param {Object} options
     */
    SceneDirector.prototype.add = function(scene, options) {

      // Shorthand to add all scenes to the scene director
      if (scene === '*' && this.scenes) {
        scene = Object.keys(this.scenes);
      }

      // Allow array as scenes
      if (Array.isArray(scene)) {
        for (var i = 0, j = scene.length; i < j; i++) {
          this.add(scene[i], options);
        }

        return;
      }

      if (!( scene instanceof Scene)) {
        if (this.scenes && !$.isEmptyObject(this.scenes)) {
          scene = this.scenes[scene];
        } else {
          // TODO: Don't throw a string. Throw an error
          throw 'No valid scene found.';
        }
      }

      scene.parent = this;

      // Update reference to the game itself
      if (this.parent != null) {
        scene.game = this.parent;
      }

      // Add first scene as a default scene
      if (Object.keys(this.sceneList).length === 0) {
        this.defaultScene = scene;
      }

      this.sceneList[scene.name] = scene;

      if (this.viewport.$element) {
        if ($('#' + scene.name).length === 0) {
          this.viewport.$element.prepend($(root.document.createElement('div')).attr('id', scene.name).attr('class', SceneDirector.prototype.sceneClassName));

          if (!scene.isAsync) {
            scene.trigger('added');
          }

        }
      }

      return this;
    };

    /**
     * Shows a specified scene
     *
     * @method show
     * @param {String} scene name of scene
     * @param {Object} options
     * @param {Function} callback
     */
    SceneDirector.prototype.show = function(sceneName, options, callback) {
      // More than one scene visible at the same time
      if ($('.' + SceneDirector.prototype.sceneClassName + ':visible')) {
        $('.' + SceneDirector.prototype.sceneClassName).hide();
      }

      if (this.currentScene) {
        if (this.currentScene.transition && this.currentScene.length) {
          $('#' + this.currentScene).hide(this.currentScene.transition.length, function() {
            $('.' + SceneDirector.prototype.sceneClassName).hide();
          });
        } else {
          $('.' + SceneDirector.prototype.sceneClassName).hide();
        }

        this.currentScene.trigger('deactivate', options);
      }

      var self = this;

      self.currentScene = this.sceneList[sceneName];

      if (self.currentScene.transition && self.currentScene.transition.length) {
        $('#' + sceneName).show(self.currentScene.transition.length);
      } else {
        $('#' + sceneName).show();
      }
      this.trigger('scene:change', sceneName);
      self.currentScene.trigger('active', options);

      if (callback) {
        callback(sceneName);
      }
    };

    /**
     * Refreshes a scene
     *
     * @method refresh
     * @param {String} scene
     */
    SceneDirector.prototype.refresh = function(scene) {
      var sceneObj = (scene) ? this.sceneList[scene] : this.currentScene;

      // Re-compile scene template
      sceneObj.refresh();
    };

    return SceneDirector;

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


<commit_before>(function(angular) {
	'use strict';

	var app = angular.module('bugminerApp', ['ngResource', 'ui.router', 'ui.bootstrap']);

	app.config(['$stateProvider', function($stateProvider) {
		$stateProvider
			.state('index', {
				url: '/',
				templateUrl: 'partials/projects/index.html'
			})
			.state('project', {
				url: '/projects/:name',
				templateUrl: 'partials/projects/view.html',
				resolve: {
					params: function($stateParams) {
						return $stateParams;
					}
				}
			})
			.state('project.bugs', {
				url: '/bugs?page',
				templateUrl: 'partials/projects/bugs.html',
				controller: 'ProjectBugsCtrl',
				resolve: {
					bugPage: function(BugPage, $location, params) {
						var page = $location.search().page ? $location.search().page : 1;

						return BugPage.get({page: page - 1, name: params.name}).$promise.then(function(data) {
							return {
								bugs: data.content,
								totalItems: data.totalElements,
								itemsPerPage: data.size,
								currentPage: page
							};
						});
					}
				}
			});
	}]);

	app.factory('Project', function($resource) {
		  return $resource('/api/projects/:name');
	});

	app.factory('BugPage', function($resource) {
		return $resource('/api/projects/:name/bugs', {sort: 'reportTime', size: 10});
	});

	app.factory('LineChange', function($resource, $stateParams) {
		return $resource('/api/projects/:name/bugs/:tracker/:key/diff', {name: $stateParams.name});
	});

	app.controller('ProjectsCtrl', function($scope, Project) {
		Project.query(function(data) {
			$scope.projects = data;
			console.log(data);
		});
	});

	app.controller('ProjectCtrl', function($scope) {

	});

	app.controller('ProjectBugsCtrl', function($scope, $location, bugPage, LineChange) {
		angular.extend($scope, bugPage);
		$scope.currentBug = null;
		$scope.changedFiles = [];

		$scope.$watch('currentPage', function() {
			$location.search({page: $scope.currentPage});
		});

		$scope.setCurrentBug = function(bug) {
			$scope.currentBug = bug;

			// fetch line changes
			LineChange.query({tracker: bug.issueTracker.name, key: bug.key}, function(data) {
				$scope.changedFiles = [];
				var lineChanges = data;
				var currentFileName = '';
				var currentFile = null;
				var currentEdit = {
					additions: [],
					deletions: [],
					lastLineNumber: 0
				};

				for (var i = 0; i < lineChanges.length; i++) {
					if (currentFileName !== lineChanges[i].fileName) {
						currentFileName = lineChanges[i].fileName;

						currentFile = {
							name: currentFileName,
							edits: [currentEdit]
						};

						$scope.changedFiles.push(currentFile);
					}

					if (lineChanges[i].oldLineNumber > currentEdit.lastLineNumber + 1) {
						currentEdit = {
							additions: [],
							deletions: [],
							lastLineNumber: 0
						};

						currentFile.edits.push(currentEdit);
					}

					if (lineChanges[i].kind === 'ADDITION') {
						currentEdit.additions.push(lineChanges[i]);
						currentEdit.lastLineNumber = lineChanges[i].oldLineNumber;
					} else {
						currentEdit.deletions.push(lineChanges[i]);
						currentEdit.lastLineNumber = lineChanges[i].oldLineNumber;
					}
				}

				console.log($scope.changedFiles);
			});
		};
	});

})(angular);
<commit_msg>Removed console output<commit_after>(function(angular) {
	'use strict';

	var app = angular.module('bugminerApp', ['ngResource', 'ui.router', 'ui.bootstrap']);

	app.config(['$stateProvider', function($stateProvider) {
		$stateProvider
			.state('index', {
				url: '/',
				templateUrl: 'partials/projects/index.html'
			})
			.state('project', {
				url: '/projects/:name',
				templateUrl: 'partials/projects/view.html',
				resolve: {
					params: function($stateParams) {
						return $stateParams;
					}
				}
			})
			.state('project.bugs', {
				url: '/bugs?page',
				templateUrl: 'partials/projects/bugs.html',
				controller: 'ProjectBugsCtrl',
				resolve: {
					bugPage: function(BugPage, $location, params) {
						var page = $location.search().page ? $location.search().page : 1;

						return BugPage.get({page: page - 1, name: params.name}).$promise.then(function(data) {
							return {
								bugs: data.content,
								totalItems: data.totalElements,
								itemsPerPage: data.size,
								currentPage: page
							};
						});
					}
				}
			});
	}]);

	app.factory('Project', function($resource) {
		  return $resource('/api/projects/:name');
	});

	app.factory('BugPage', function($resource) {
		return $resource('/api/projects/:name/bugs', {sort: 'reportTime', size: 10});
	});

	app.factory('LineChange', function($resource, $stateParams) {
		return $resource('/api/projects/:name/bugs/:tracker/:key/diff', {name: $stateParams.name});
	});

	app.controller('ProjectsCtrl', function($scope, Project) {
		Project.query(function(data) {
			$scope.projects = data;
		});
	});

	app.controller('ProjectCtrl', function($scope) {

	});

	app.controller('ProjectBugsCtrl', function($scope, $location, bugPage, LineChange) {
		angular.extend($scope, bugPage);
		$scope.currentBug = null;
		$scope.changedFiles = [];

		$scope.$watch('currentPage', function() {
			$location.search({page: $scope.currentPage});
		});

		$scope.setCurrentBug = function(bug) {
			$scope.currentBug = bug;

			// fetch line changes
			LineChange.query({tracker: bug.issueTracker.name, key: bug.key}, function(data) {
				$scope.changedFiles = [];
				var lineChanges = data;
				var currentFileName = '';
				var currentFile = null;
				var currentEdit = {
					additions: [],
					deletions: [],
					lastLineNumber: 0
				};

				for (var i = 0; i < lineChanges.length; i++) {
					if (currentFileName !== lineChanges[i].fileName) {
						currentFileName = lineChanges[i].fileName;

						currentFile = {
							name: currentFileName,
							edits: [currentEdit]
						};

						$scope.changedFiles.push(currentFile);
					}

					if (lineChanges[i].oldLineNumber > currentEdit.lastLineNumber + 1) {
						currentEdit = {
							additions: [],
							deletions: [],
							lastLineNumber: 0
						};

						currentFile.edits.push(currentEdit);
					}

					if (lineChanges[i].kind === 'ADDITION') {
						currentEdit.additions.push(lineChanges[i]);
						currentEdit.lastLineNumber = lineChanges[i].oldLineNumber;
					} else {
						currentEdit.deletions.push(lineChanges[i]);
						currentEdit.lastLineNumber = lineChanges[i].oldLineNumber;
					}
				}
			});
		};
	});

})(angular);
<|endoftext|>


<commit_before>'use strict';

/* global -nullFormCtrl */
var nullFormCtrl = {
  $addControl: noop,
  $removeControl: noop,
  $setValidity: noop,
  $setDirty: noop,
  $setPristine: noop
};

/**
 * @ngdoc type
 * @name form.FormController
 *
 * @property {boolean} $pristine True if user has not interacted with the form yet.
 * @property {boolean} $dirty True if user has already interacted with the form.
 * @property {boolean} $valid True if all of the containing forms and controls are valid.
 * @property {boolean} $invalid True if at least one containing control or form is invalid.
 *
 * @property {Object} $error Is an object hash, containing references to all invalid controls or
 *  forms, where:
 *
 *  - keys are validation tokens (error names),
 *  - values are arrays of controls or forms that are invalid for given error name.
 *
 *
 *  Built-in validation tokens:
 *
 *  - `email`
 *  - `max`
 *  - `maxlength`
 *  - `min`
 *  - `minlength`
 *  - `number`
 *  - `pattern`
 *  - `required`
 *  - `url`
 *
 * @description
 * `FormController` keeps track of all its controls and nested forms as well as state of them,
 * such as being valid/invalid or dirty/pristine.
 *
 * Each {@link ng.directive:form form} directive creates an instance
 * of `FormController`.
 *
 */
//asks for $scope to fool the BC controller module
FormController.$inject = ['$element', '$attrs', '$scope', '$animate'];
function FormController(element, attrs, $scope, $animate) {
  var form = this,
      parentForm = element.parent().controller('form') || nullFormCtrl,
      invalidCount = 0, // used to easily determine if we are valid
      errors = form.$error = {},
      controls = [];

  // init state
  form.$name = attrs.name || attrs.ngForm;
  form.$dirty = false;
  form.$pristine = true;
  form.$valid = true;
  form.$invalid = false;

  parentForm.$addControl(form);

  // Setup initial state of the control
  element.addClass(PRISTINE_CLASS);
  toggleValidCss(true);

  // convenience method for easy toggling of classes
  function toggleValidCss(isValid, validationErrorKey) {
    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
    $animate.removeClass(element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
    $animate.addClass(element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
  }

  /**
   * @ngdoc method
   * @name form.FormController#$addControl
   *
   * @description
   * Register a control with the form.
   *
   * Input elements using ngModelController do this automatically when they are linked.
   */
  form.$addControl = function(control) {
    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
    // and not added to the scope.  Now we throw an error.
    assertNotHasOwnProperty(control.$name, 'input');
    controls.push(control);

    if (control.$name) {
      form[control.$name] = control;
    }
  };

  /**
   * @ngdoc method
   * @name form.FormController#$removeControl
   *
   * @description
   * Deregister a control from the form.
   *
   * Input elements using ngModelController do this automatically when they are destroyed.
   */
  form.$removeControl = function(control) {
    if (control.$name && form[control.$name] === control) {
      delete form[control.$name];
    }
    forEach(errors, function(queue, validationToken) {
      form.$setValidity(validationToken, true, control);
    });

    arrayRemove(controls, control);
  };

  /**
   * @ngdoc method
   * @name form.FormController#$setValidity
   *
   * @description
   * Sets the validity of a form control.
   *
   * This method will also propagate to parent forms.
   */
  form.$setValidity = function(validationToken, isValid, control) {
    var queue = errors[validationToken];

    if (isValid) {
      if (queue) {
        arrayRemove(queue, control);
        if (!queue.length) {
          invalidCount--;
          if (!invalidCount) {
            toggleValidCss(isValid);
            form.$valid = true;
            form.$invalid = false;
          }
          errors[validationToken] = false;
          toggleValidCss(true, validationToken);
          parentForm.$setValidity(validationToken, true, form);
        }
      }

    } else {
      if (!invalidCount) {
        toggleValidCss(isValid);
      }
      if (queue) {
        if (includes(queue, control)) return;
      } else {
        errors[validationToken] = queue = [];
        invalidCount++;
        toggleValidCss(false, validationToken);
        parentForm.$setValidity(validationToken, false, form);
      }
      queue.push(control);

      form.$valid = false;
      form.$invalid = true;
    }
  };

  /**
   * @ngdoc method
   * @name form.FormController#$setDirty
   *
   * @description
   * Sets the form to a dirty state.
   *
   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
   * state (ng-dirty class). This method will also propagate to parent forms.
   */
  form.$setDirty = function() {
    $animate.removeClass(element, PRISTINE_CLASS);
    $animate.addClass(element, DIRTY_CLASS);
    form.$dirty = true;
    form.$pristine = false;
    parentForm.$setDirty();
  };

  /**
   * @ngdoc method
   * @name form.FormController#$setPristine
   *
   * @description
   * Sets the form to its pristine state.
   *
   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
   * state (ng-pristine class). This method will also propagate to all the controls contained
   * in this form.
   *
   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
   * saving or resetting it.
   */
  form.$setPristine = function () {
    $animate.removeClass(element, DIRTY_CLASS);
    $animate.addClass(element, PRISTINE_CLASS);
    form.$dirty = false;
    form.$pristine = true;
    forEach(controls, function(control) {
      control.$setPristine();
    });
  };
}

/**
 * @ngdoc directive
 * @name ngForm
 * @restrict EAC
 *
 * @description
 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
 * sub-group of controls needs to be determined.
 *
 * Note: the purpose of `ngForm` is to group controls,
 * but not to be a replacement for the `<form>` tag with all of its capabilities
 * (e.g. posting to the server, ...).
 *
 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
 *                       related scope, under this name.
 *
 */

 /**
 * @ngdoc directive
 * @name form
 * @restrict E
 *
 * @description
 * Directive that instantiates
 * {@link form.FormController FormController}.
 *
 * If the `name` attribute is specified, the form controller is published onto the current scope under
 * this name.
 *
 * # Alias: {@link ng.directive:ngForm `ngForm`}
 *
 * In Angular forms can be nested. This means that the outer form is valid when all of the child
 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
 * using Angular validation directives in forms that are dynamically generated using the
 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
 * `ngForm` directive and nest these in an outer `form` element.
 *
 *
 * # CSS classes
 *  - `ng-valid` is set if the form is valid.
 *  - `ng-invalid` is set if the form is invalid.
 *  - `ng-pristine` is set if the form is pristine.
 *  - `ng-dirty` is set if the form is dirty.
 *
 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
 *
 *
 * # Submitting a form and preventing the default action
 *
 * Since the role of forms in client-side Angular applications is different than in classical
 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
 * page reload that sends the data to the server. Instead some javascript logic should be triggered
 * to handle the form submission in an application-specific way.
 *
 * For this reason, Angular prevents the default action (form submission to the server) unless the
 * `<form>` element has an `action` attribute specified.
 *
 * You can use one of the following two ways to specify what javascript method should be called when
 * a form is submitted:
 *
 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
 * - {@link ng.directive:ngClick ngClick} directive on the first
  *  button or input field of type submit (input[type=submit])
 *
 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
 * or {@link ng.directive:ngClick ngClick} directives.
 * This is because of the following form submission rules in the HTML specification:
 *
 * - If a form has only one input field then hitting enter in this field triggers form submit
 * (`ngSubmit`)
 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
 * doesn't trigger submit
 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
 *
 * @param {string=} name Name of the form. If specified, the form controller will be published into
 *                       related scope, under this name.
 *
 * ## Animation Hooks
 *
 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
 * other validations that are performed within the form. Animations in ngForm are similar to how
 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
 * as JS animations.
 *
 * The following example shows a simple way to utilize CSS transitions to style a form element
 * that has been rendered as invalid after it has been validated:
 *
 *

 * //be sure to include ngAnimate as a module to hook into more
 * //advanced animations
 * .my-form {
 *   transition:0.5s linear all;
 *   background: white;
 * }
 * .my-form.ng-invalid {
 *   background: red;
 *   color:white;
 * }
 *

 *
 * @example
    <example deps="angular-animate.js" animations="true" fixBase="true">
      <file name="index.html">

       <form name="myForm" ng-controller="Ctrl" class="my-form">
         userType: <input name="input" ng-model="userType" required>
         Required!

         <tt>userType = {{userType}}</tt>

         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt>

         <tt>myForm.input.$error = {{myForm.input.$error}}</tt>

         <tt>myForm.$valid = {{myForm.$valid}}</tt>

         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt>

        </form>
      </file>
      <file name="protractor.js" type="protractor">
        it('should initialize to model', function() {
          var userType = element(by.binding('userType'));
          var valid = element(by.binding('myForm.input.$valid'));

          expect(userType.getText()).toContain('guest');
          expect(valid.getText()).toContain('true');
        });

        it('should be invalid if empty', function() {
          var userType = element(by.binding('userType'));
          var valid = element(by.binding('myForm.input.$valid'));
          var userInput = element(by.model('userType'));

          userInput.clear();
          userInput.sendKeys('');

          expect(userType.getText()).toEqual('userType =');
          expect(valid.getText()).toContain('false');
        });
      </file>
    </example>
 *
 */
var formDirectiveFactory = function(isNgForm) {
  return ['$timeout', function($timeout) {
    var formDirective = {
      name: 'form',
      restrict: isNgForm ? 'EAC' : 'E',
      controller: FormController,
      compile: function() {
        return {
          pre: function(scope, formElement, attr, controller) {
            if (!attr.action) {
              // we can't use jq events because if a form is destroyed during submission the default
              // action is not prevented. see #1238
              //
              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
              // page reload if the form was destroyed by submission of the form via a click handler
              // on a button in the form. Looks like an IE9 specific bug.
              var preventDefaultListener = function(event) {
                event.preventDefault
                  ? event.preventDefault()
                  : event.returnValue = false; // IE
              };

              addEventListenerFn(formElement[0], 'submit', preventDefaultListener);

              // unregister the preventDefault listener so that we don't not leak memory but in a
              // way that will achieve the prevention of the default action.
              formElement.on('$destroy', function() {
                $timeout(function() {
                  removeEventListenerFn(formElement[0], 'submit', preventDefaultListener);
                }, 0, false);
              });
            }

            var parentFormCtrl = formElement.parent().controller('form'),
                alias = attr.name || attr.ngForm;

            if (alias) {
              setter(scope, alias, controller, alias);
            }
            if (parentFormCtrl) {
              formElement.on('$destroy', function() {
                parentFormCtrl.$removeControl(controller);
                if (alias) {
                  setter(scope, alias, undefined, alias);
                }
                extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
              });
            }
          }
        };
      }
    };

    return formDirective;
  }];
};

var formDirective = formDirectiveFactory();
var ngFormDirective = formDirectiveFactory(true);
<commit_msg>docs(form.FormController): fix grammar<commit_after>'use strict';

/* global -nullFormCtrl */
var nullFormCtrl = {
  $addControl: noop,
  $removeControl: noop,
  $setValidity: noop,
  $setDirty: noop,
  $setPristine: noop
};

/**
 * @ngdoc type
 * @name form.FormController
 *
 * @property {boolean} $pristine True if user has not interacted with the form yet.
 * @property {boolean} $dirty True if user has already interacted with the form.
 * @property {boolean} $valid True if all of the containing forms and controls are valid.
 * @property {boolean} $invalid True if at least one containing control or form is invalid.
 *
 * @property {Object} $error Is an object hash, containing references to all invalid controls or
 *  forms, where:
 *
 *  - keys are validation tokens (error names),
 *  - values are arrays of controls or forms that are invalid for given error name.
 *
 *
 *  Built-in validation tokens:
 *
 *  - `email`
 *  - `max`
 *  - `maxlength`
 *  - `min`
 *  - `minlength`
 *  - `number`
 *  - `pattern`
 *  - `required`
 *  - `url`
 *
 * @description
 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
 * such as being valid/invalid or dirty/pristine.
 *
 * Each {@link ng.directive:form form} directive creates an instance
 * of `FormController`.
 *
 */
//asks for $scope to fool the BC controller module
FormController.$inject = ['$element', '$attrs', '$scope', '$animate'];
function FormController(element, attrs, $scope, $animate) {
  var form = this,
      parentForm = element.parent().controller('form') || nullFormCtrl,
      invalidCount = 0, // used to easily determine if we are valid
      errors = form.$error = {},
      controls = [];

  // init state
  form.$name = attrs.name || attrs.ngForm;
  form.$dirty = false;
  form.$pristine = true;
  form.$valid = true;
  form.$invalid = false;

  parentForm.$addControl(form);

  // Setup initial state of the control
  element.addClass(PRISTINE_CLASS);
  toggleValidCss(true);

  // convenience method for easy toggling of classes
  function toggleValidCss(isValid, validationErrorKey) {
    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
    $animate.removeClass(element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
    $animate.addClass(element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
  }

  /**
   * @ngdoc method
   * @name form.FormController#$addControl
   *
   * @description
   * Register a control with the form.
   *
   * Input elements using ngModelController do this automatically when they are linked.
   */
  form.$addControl = function(control) {
    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
    // and not added to the scope.  Now we throw an error.
    assertNotHasOwnProperty(control.$name, 'input');
    controls.push(control);

    if (control.$name) {
      form[control.$name] = control;
    }
  };

  /**
   * @ngdoc method
   * @name form.FormController#$removeControl
   *
   * @description
   * Deregister a control from the form.
   *
   * Input elements using ngModelController do this automatically when they are destroyed.
   */
  form.$removeControl = function(control) {
    if (control.$name && form[control.$name] === control) {
      delete form[control.$name];
    }
    forEach(errors, function(queue, validationToken) {
      form.$setValidity(validationToken, true, control);
    });

    arrayRemove(controls, control);
  };

  /**
   * @ngdoc method
   * @name form.FormController#$setValidity
   *
   * @description
   * Sets the validity of a form control.
   *
   * This method will also propagate to parent forms.
   */
  form.$setValidity = function(validationToken, isValid, control) {
    var queue = errors[validationToken];

    if (isValid) {
      if (queue) {
        arrayRemove(queue, control);
        if (!queue.length) {
          invalidCount--;
          if (!invalidCount) {
            toggleValidCss(isValid);
            form.$valid = true;
            form.$invalid = false;
          }
          errors[validationToken] = false;
          toggleValidCss(true, validationToken);
          parentForm.$setValidity(validationToken, true, form);
        }
      }

    } else {
      if (!invalidCount) {
        toggleValidCss(isValid);
      }
      if (queue) {
        if (includes(queue, control)) return;
      } else {
        errors[validationToken] = queue = [];
        invalidCount++;
        toggleValidCss(false, validationToken);
        parentForm.$setValidity(validationToken, false, form);
      }
      queue.push(control);

      form.$valid = false;
      form.$invalid = true;
    }
  };

  /**
   * @ngdoc method
   * @name form.FormController#$setDirty
   *
   * @description
   * Sets the form to a dirty state.
   *
   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
   * state (ng-dirty class). This method will also propagate to parent forms.
   */
  form.$setDirty = function() {
    $animate.removeClass(element, PRISTINE_CLASS);
    $animate.addClass(element, DIRTY_CLASS);
    form.$dirty = true;
    form.$pristine = false;
    parentForm.$setDirty();
  };

  /**
   * @ngdoc method
   * @name form.FormController#$setPristine
   *
   * @description
   * Sets the form to its pristine state.
   *
   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
   * state (ng-pristine class). This method will also propagate to all the controls contained
   * in this form.
   *
   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
   * saving or resetting it.
   */
  form.$setPristine = function () {
    $animate.removeClass(element, DIRTY_CLASS);
    $animate.addClass(element, PRISTINE_CLASS);
    form.$dirty = false;
    form.$pristine = true;
    forEach(controls, function(control) {
      control.$setPristine();
    });
  };
}

/**
 * @ngdoc directive
 * @name ngForm
 * @restrict EAC
 *
 * @description
 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
 * sub-group of controls needs to be determined.
 *
 * Note: the purpose of `ngForm` is to group controls,
 * but not to be a replacement for the `<form>` tag with all of its capabilities
 * (e.g. posting to the server, ...).
 *
 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
 *                       related scope, under this name.
 *
 */

 /**
 * @ngdoc directive
 * @name form
 * @restrict E
 *
 * @description
 * Directive that instantiates
 * {@link form.FormController FormController}.
 *
 * If the `name` attribute is specified, the form controller is published onto the current scope under
 * this name.
 *
 * # Alias: {@link ng.directive:ngForm `ngForm`}
 *
 * In Angular forms can be nested. This means that the outer form is valid when all of the child
 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
 * using Angular validation directives in forms that are dynamically generated using the
 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
 * `ngForm` directive and nest these in an outer `form` element.
 *
 *
 * # CSS classes
 *  - `ng-valid` is set if the form is valid.
 *  - `ng-invalid` is set if the form is invalid.
 *  - `ng-pristine` is set if the form is pristine.
 *  - `ng-dirty` is set if the form is dirty.
 *
 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
 *
 *
 * # Submitting a form and preventing the default action
 *
 * Since the role of forms in client-side Angular applications is different than in classical
 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
 * page reload that sends the data to the server. Instead some javascript logic should be triggered
 * to handle the form submission in an application-specific way.
 *
 * For this reason, Angular prevents the default action (form submission to the server) unless the
 * `<form>` element has an `action` attribute specified.
 *
 * You can use one of the following two ways to specify what javascript method should be called when
 * a form is submitted:
 *
 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
 * - {@link ng.directive:ngClick ngClick} directive on the first
  *  button or input field of type submit (input[type=submit])
 *
 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
 * or {@link ng.directive:ngClick ngClick} directives.
 * This is because of the following form submission rules in the HTML specification:
 *
 * - If a form has only one input field then hitting enter in this field triggers form submit
 * (`ngSubmit`)
 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
 * doesn't trigger submit
 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
 *
 * @param {string=} name Name of the form. If specified, the form controller will be published into
 *                       related scope, under this name.
 *
 * ## Animation Hooks
 *
 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
 * other validations that are performed within the form. Animations in ngForm are similar to how
 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
 * as JS animations.
 *
 * The following example shows a simple way to utilize CSS transitions to style a form element
 * that has been rendered as invalid after it has been validated:
 *
 *

 * //be sure to include ngAnimate as a module to hook into more
 * //advanced animations
 * .my-form {
 *   transition:0.5s linear all;
 *   background: white;
 * }
 * .my-form.ng-invalid {
 *   background: red;
 *   color:white;
 * }
 *

 *
 * @example
    <example deps="angular-animate.js" animations="true" fixBase="true">
      <file name="index.html">

       <form name="myForm" ng-controller="Ctrl" class="my-form">
         userType: <input name="input" ng-model="userType" required>
         Required!

         <tt>userType = {{userType}}</tt>

         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt>

         <tt>myForm.input.$error = {{myForm.input.$error}}</tt>

         <tt>myForm.$valid = {{myForm.$valid}}</tt>

         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt>

        </form>
      </file>
      <file name="protractor.js" type="protractor">
        it('should initialize to model', function() {
          var userType = element(by.binding('userType'));
          var valid = element(by.binding('myForm.input.$valid'));

          expect(userType.getText()).toContain('guest');
          expect(valid.getText()).toContain('true');
        });

        it('should be invalid if empty', function() {
          var userType = element(by.binding('userType'));
          var valid = element(by.binding('myForm.input.$valid'));
          var userInput = element(by.model('userType'));

          userInput.clear();
          userInput.sendKeys('');

          expect(userType.getText()).toEqual('userType =');
          expect(valid.getText()).toContain('false');
        });
      </file>
    </example>
 *
 */
var formDirectiveFactory = function(isNgForm) {
  return ['$timeout', function($timeout) {
    var formDirective = {
      name: 'form',
      restrict: isNgForm ? 'EAC' : 'E',
      controller: FormController,
      compile: function() {
        return {
          pre: function(scope, formElement, attr, controller) {
            if (!attr.action) {
              // we can't use jq events because if a form is destroyed during submission the default
              // action is not prevented. see #1238
              //
              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
              // page reload if the form was destroyed by submission of the form via a click handler
              // on a button in the form. Looks like an IE9 specific bug.
              var preventDefaultListener = function(event) {
                event.preventDefault
                  ? event.preventDefault()
                  : event.returnValue = false; // IE
              };

              addEventListenerFn(formElement[0], 'submit', preventDefaultListener);

              // unregister the preventDefault listener so that we don't not leak memory but in a
              // way that will achieve the prevention of the default action.
              formElement.on('$destroy', function() {
                $timeout(function() {
                  removeEventListenerFn(formElement[0], 'submit', preventDefaultListener);
                }, 0, false);
              });
            }

            var parentFormCtrl = formElement.parent().controller('form'),
                alias = attr.name || attr.ngForm;

            if (alias) {
              setter(scope, alias, controller, alias);
            }
            if (parentFormCtrl) {
              formElement.on('$destroy', function() {
                parentFormCtrl.$removeControl(controller);
                if (alias) {
                  setter(scope, alias, undefined, alias);
                }
                extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
              });
            }
          }
        };
      }
    };

    return formDirective;
  }];
};

var formDirective = formDirectiveFactory();
var ngFormDirective = formDirectiveFactory(true);
<|endoftext|>


<commit_before>module.exports = {
  registryUrl: 'https://registry.npmjs.cf/',
  autoCompleteUrl: 'http://npm-cors-proxy.herokuapp.com/registry/_design/scratch/_view/byField'
}
<commit_msg>Fixed https link<commit_after>module.exports = {
  registryUrl: 'https://registry.npmjs.cf/',
  autoCompleteUrl: 'https://npm-cors-proxy.herokuapp.com/registry/_design/scratch/_view/byField'
}
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import defaultPropTypes from './defaultPropTypes';
import StackedNotification from './stackedNotification';

/**
 * The notification list does not have any state, so use a
 * pure function here. It just needs to return the stacked array
 * of notification components.
 */
const NotificationStack = props => {
  return (

      {props.notifications.map((notification, index) => {
        const dismissAfter = notification.dismissAfter || props.dismissAfter;
        const isLast = index === 0 && props.notifications.length === 1;

        return (
          <StackedNotification
            {...notification}
            key={notification.key}
            isLast={isLast}
            action={notification.action || props.action}
            dismissAfter={isLast ? dismissAfter : dismissAfter + (index * 1000)}
            onDismiss={props.onDismiss.bind(this, notification)}
            index={index}
          />
        );
      })}

  )
};

NotificationStack.propTypes = {
  notifications: PropTypes.array.isRequired,
  onDismiss: PropTypes.func
};

NotificationStack.defaultProps = {
  dismissAfter: 1000
}

export default NotificationStack;
<commit_msg>Make onDismiss required<commit_after>import React, { PropTypes } from 'react';
import defaultPropTypes from './defaultPropTypes';
import StackedNotification from './stackedNotification';

/**
 * The notification list does not have any state, so use a
 * pure function here. It just needs to return the stacked array
 * of notification components.
 */
const NotificationStack = props => {
  return (

      {props.notifications.map((notification, index) => {
        const dismissAfter = notification.dismissAfter || props.dismissAfter;
        const isLast = index === 0 && props.notifications.length === 1;

        return (
          <StackedNotification
            {...notification}
            key={notification.key}
            isLast={isLast}
            action={notification.action || props.action}
            dismissAfter={isLast ? dismissAfter : dismissAfter + (index * 1000)}
            onDismiss={props.onDismiss.bind(this, notification)}
            index={index}
          />
        );
      })}

  )
};

NotificationStack.propTypes = {
  notifications: PropTypes.array.isRequired,
  onDismiss: PropTypes.func.isRequired
};

NotificationStack.defaultProps = {
  dismissAfter: 1000
}

export default NotificationStack;
<|endoftext|>


<commit_before>var util = require('util');

var Client = require('../rollbar');
var _ = require('../utility');
var logger = require('./logger');

var transforms = require('./transforms');
var predicates = require('./predicates');

function Rollbar(accessToken, options, client) {
  options = options || {};
  options.accessToken = accessToken;
  this.context = 'server';
  this.options = _.extend(true, {}, this.options, options);
  this.options.environment = this.options.environment || process.env.NODE_ENV || 'unspecified';
  this.client = client || new Client(this.context, this.options);
  addTransformsToNotifier(this.client.notifier);
  addPredicatesToQueue(this.client.queue);

  if (this.options.handleUncaughtExceptions) {
    this.handleUncaughtExceptions();
  }
  if (this.optiions.handleUnhandledRejections) {
    this.handleUnhandledRejects();
  }
}

Rollbar.prototype.global = function(options) {
  this.client.global(options);
};

Rollbar.prototype.configure = function(options) {
  this.options = _.extend(true, {}, this.options, options);
  this.client.configure(options);
};

Rollbar.prototype.log = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.log(item);
  return {uuid: uuid};
};

Rollbar.prototype.debug = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.debug(item);
  return {uuid: uuid};
};

Rollbar.prototype.info = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.info(item);
  return {uuid: uuid};
};

Rollbar.prototype.warn = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.warn(item);
  return {uuid: uuid};
};

Rollbar.prototype.warning = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.warning(item);
  return {uuid: uuid};
};

Rollbar.prototype.error = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.error(item);
  return {uuid: uuid};
};

Rollbar.prototype.critical = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.critical(item);
  return {uuid: uuid};
};

Rollbar.prototype.errorHandler = function(err, request, response, next) {
  var cb = function(rollbarError) {
    if (rollbarError) {
      logger.error('Error reporting to rollbar, ignoring: ' + rollbarError);
    }
    return next(err, request, response);
  };
  if (!err) {
    return next(err, request, response);
  }

  if (err instanceof Error) {
    return this.error(err, request, cb);
  }
  return this.error('Error: ' + err, request, cb);
};

/** DEPRECATED **/

Rollbar.prototype.reportMessage = function(message, level, request, callback) {
  if (_.isFunction(this[level])) {
    return this[level](message, request, callback);
  } else {
    return this.error(message, request, callback);
  }
};

Rollbar.prototype.reportMessageWithPayloadData = function(message, payloadData, request, callback) {
  return this.error(message, request, payloadData, callback);
};

Rollbar.prototype.handleError = function(err, request, callback) {
  return this.error(err, request, callback);
};

Rollbar.prototype.handleErrorWithPayloadData = function(err, payloadData, request, callback) {
  return this.error(err, request, payloadData, callback);
};

/** Internal **/

function addTransformsToNotifier(notifier) {
  notifier
    .addTransform(transforms.baseData)
    .addTransform(transforms.addMessageData)
    .addTransform(transforms.buildErrorData)
    .addTransform(transforms.addRequestData)
    .addTransform(transforms.convertToPayload);
}

function addPredicatesToQueue(queue) {
  queue
    .addPredicate(predicates.checkLevel);
}

/*
 * message/error, callback
 * message/error, request
 * message/error, request, callback
 * message/error, request, custom
 * message/error, request, custom, callback
 */
Rollbar.prototype._createItem = function(args) {
  var messageOrError = args[0];
  var request = args[1];
  var custom = args[2];
  var callback = args[3];

  if (_.isFunction(request) && !callback) {
    callback = request;
    request = null;
  }

  if (_.isFunction(custom) && !callback) {
    callback = custom;
    custom = null;
  }

  var message, err;
  if (_.isType(messageOrError, 'error')) {
    err = messageOrError;
  } else {
    message = messageOrError;
  }

  var item = {
    uuid: _.uuid4(),
    err: err,
    message: message,
    callback: callback,
    request: request
  };
  if (custom.level !== undefined) {
    item.level = custom.level;
    delete custom.level;
  }
  item.custom = custom;
  return item;
};

Rollbar.prototype.handleUncaughtExceptions = function() {
  var exitOnUncaught = !!this.options.exitOnUncaughtException;
  delete this.options.exitOnUncaughtException;

  process.on('uncaughtException', function(err) {
    this.error(err, function(err) {
      if (err) {
        logger.error('Encountered error while handling an uncaught exception.');
        logger.error(err);
      }

      if (exitOnUncaught) {
        process.exit(1);
      }
    });
  }.bind(this));
};

Rollbar.prototype.handleUnhandledRejections = function() {
  process.on('unhandledRejection', function(reason) {
    this.error(reason, function(err) {
      if (err) {
        logger.error('Encountered error while handling an uncaught exception.');
        logger.error(err);
      }
    });
  }.bind(this));
};

function RollbarError(message, nested) {
  Error.call(this);
  Error.captureStackTrace(this, this.constructor);

  this.message = message;
  this.nested = nested;
  this.name = this.constructor.name;
}
util.inherits(RollbarError, Error);
Rollbar.Error = RollbarError;

module.exports = Rollbar;
<commit_msg>make sure server side uncaught exceptions and rejections only get listened for once<commit_after>var util = require('util');

var Client = require('../rollbar');
var _ = require('../utility');
var logger = require('./logger');

var transforms = require('./transforms');
var predicates = require('./predicates');

function Rollbar(accessToken, options, client) {
  options = options || {};
  options.accessToken = accessToken;
  this.context = 'server';
  this.options = _.extend(true, {}, this.options, options);
  this.options.environment = this.options.environment || process.env.NODE_ENV || 'unspecified';
  this.client = client || new Client(this.context, this.options);
  addTransformsToNotifier(this.client.notifier);
  addPredicatesToQueue(this.client.queue);

  if (this.options.handleUncaughtExceptions) {
    this.handleUncaughtExceptions();
  }
  if (this.optiions.handleUnhandledRejections) {
    this.handleUnhandledRejects();
  }
}

Rollbar.prototype.global = function(options) {
  this.client.global(options);
};

Rollbar.prototype.configure = function(options) {
  this.options = _.extend(true, {}, this.options, options);
  this.client.configure(options);
};

Rollbar.prototype.log = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.log(item);
  return {uuid: uuid};
};

Rollbar.prototype.debug = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.debug(item);
  return {uuid: uuid};
};

Rollbar.prototype.info = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.info(item);
  return {uuid: uuid};
};

Rollbar.prototype.warn = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.warn(item);
  return {uuid: uuid};
};

Rollbar.prototype.warning = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.warning(item);
  return {uuid: uuid};
};

Rollbar.prototype.error = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.error(item);
  return {uuid: uuid};
};

Rollbar.prototype.critical = function() {
  var item = this._createItem(arguments);
  var uuid = item.uuid;
  this.client.critical(item);
  return {uuid: uuid};
};

Rollbar.prototype.errorHandler = function(err, request, response, next) {
  var cb = function(rollbarError) {
    if (rollbarError) {
      logger.error('Error reporting to rollbar, ignoring: ' + rollbarError);
    }
    return next(err, request, response);
  };
  if (!err) {
    return next(err, request, response);
  }

  if (err instanceof Error) {
    return this.error(err, request, cb);
  }
  return this.error('Error: ' + err, request, cb);
};

/** DEPRECATED **/

Rollbar.prototype.reportMessage = function(message, level, request, callback) {
  if (_.isFunction(this[level])) {
    return this[level](message, request, callback);
  } else {
    return this.error(message, request, callback);
  }
};

Rollbar.prototype.reportMessageWithPayloadData = function(message, payloadData, request, callback) {
  return this.error(message, request, payloadData, callback);
};

Rollbar.prototype.handleError = function(err, request, callback) {
  return this.error(err, request, callback);
};

Rollbar.prototype.handleErrorWithPayloadData = function(err, payloadData, request, callback) {
  return this.error(err, request, payloadData, callback);
};

/** Internal **/

function addTransformsToNotifier(notifier) {
  notifier
    .addTransform(transforms.baseData)
    .addTransform(transforms.addMessageData)
    .addTransform(transforms.buildErrorData)
    .addTransform(transforms.addRequestData)
    .addTransform(transforms.convertToPayload);
}

function addPredicatesToQueue(queue) {
  queue
    .addPredicate(predicates.checkLevel);
}

/*
 * message/error, callback
 * message/error, request
 * message/error, request, callback
 * message/error, request, custom
 * message/error, request, custom, callback
 */
Rollbar.prototype._createItem = function(args) {
  var messageOrError = args[0];
  var request = args[1];
  var custom = args[2];
  var callback = args[3];

  if (_.isFunction(request) && !callback) {
    callback = request;
    request = null;
  }

  if (_.isFunction(custom) && !callback) {
    callback = custom;
    custom = null;
  }

  var message, err;
  if (_.isType(messageOrError, 'error')) {
    err = messageOrError;
  } else {
    message = messageOrError;
  }

  var item = {
    uuid: _.uuid4(),
    err: err,
    message: message,
    callback: callback,
    request: request
  };
  if (custom.level !== undefined) {
    item.level = custom.level;
    delete custom.level;
  }
  item.custom = custom;
  return item;
};

Rollbar.prototype.handleUncaughtExceptions = function() {
  var exitOnUncaught = !!this.options.exitOnUncaughtException;
  delete this.options.exitOnUncaughtException;

  addOrReplaceRollbarHandler('uncaughtException', function(err) {
    this.error(err, function(err) {
      if (err) {
        logger.error('Encountered error while handling an uncaught exception.');
        logger.error(err);
      }

      if (exitOnUncaught) {
        process.exit(1);
      }
    });
  }.bind(this));
};

Rollbar.prototype.handleUnhandledRejections = function() {
  addOrReplaceRollbarHandler('unhandledRejection', function(reason) {
    this.error(reason, function(err) {
      if (err) {
        logger.error('Encountered error while handling an uncaught exception.');
        logger.error(err);
      }
    });
  }.bind(this));
};

function addOrReplaceRollbarHandler(event, action) {
  // We only support up to two arguments which is enough for how this is used
  // rather than dealing with `arguments` and `apply`
  var fn = function(a, b) {
    action(a, b);
  };
  fn._rollbarHandler = true;

  var listeners = process.listeners(event);
  var len = listeners.length;
  for (var i = 0; i < len; ++i) {
    if (listeners[i]._rollbarHandler) {
      process.removeListener(event, listeners[i]);
    }
  }
  process.on(event, fn);
}

function RollbarError(message, nested) {
  Error.call(this);
  Error.captureStackTrace(this, this.constructor);

  this.message = message;
  this.nested = nested;
  this.name = this.constructor.name;
}
util.inherits(RollbarError, Error);
Rollbar.Error = RollbarError;

module.exports = Rollbar;
<|endoftext|>


<commit_before>// @flow

import { mainStory, config } from 'storyboard/lib/withConsoleListener';
import program from 'commander';
import opn from 'opn';
import * as httpServer from './httpServer';
import * as extractor from './extractor';

config({ filter: '*:INFO' });

const pkg = require('../../package.json');

const DEFAULT_PORT = 8080;
const DEFAULT_SNAPSHOT_PATTERNS = '**/*.snap,!node_modules/**/*';
const DEFAULT_CSS_PATTERNS = 'snapshot.css';

process.on('SIGINT', () => {
  mainStory.debug('startup', 'CTRL-C received');
  process.exit(0);
});

// ==============================================
// Main
// ==============================================
program
  .version(pkg.version)
  .option('-f --snapshot-patterns [globs]',
    'Glob patterns for snapshot files (comma-separated)',
    DEFAULT_SNAPSHOT_PATTERNS)
  .option('-c --css-patterns [globs]',
    'Glob patterns for CSS stylesheets ' +
    'that will be used for ALL snapshots (comma-separated)',
    DEFAULT_CSS_PATTERNS)
  .option('-p, --port [port]', 'Initial port number to use ' +
    '(if unavailable, the next available one will be used)', Number, DEFAULT_PORT)
  .option('--no-watch', 'Do not watch initially detected snapshot and css files')
  .parse(process.argv);

const cliOptions = program.opts();
cliOptions.snapshotPatterns = cliOptions.snapshotPatterns.split(/\s*,\s*/);
cliOptions.cssPatterns = cliOptions.cssPatterns.split(/\s*,\s*/);

mainStory.info('startup', 'CLI options:', { attach: cliOptions });

let finalPort;
let socketioServer;
const httpInit = () =>
  httpServer.init({ port: cliOptions.port })
  .then((port) => {
    finalPort = port;
    socketioServer = httpServer.getSocketioServer();
  });

const extractorInit = () => {
  extractor.configure({
    snapshotPatterns: cliOptions.snapshotPatterns,
    cssPatterns: cliOptions.cssPatterns,
    watch: cliOptions.watch,
    socketioServer,
  });
  return extractor.start();
};

Promise.resolve()
.then(httpInit)
.then(extractorInit)
.then(() => { opn(`http://localhost:${finalPort}/`); });
<commit_msg>Add babel-polyfill to the server side<commit_after>// @flow

import 'babel-polyfill';
import { mainStory, config } from 'storyboard/lib/withConsoleListener';
import program from 'commander';
import opn from 'opn';
import * as httpServer from './httpServer';
import * as extractor from './extractor';

config({ filter: '*:INFO' });

const pkg = require('../../package.json');

const DEFAULT_PORT = 8080;
const DEFAULT_SNAPSHOT_PATTERNS = '**/*.snap,!node_modules/**/*';
const DEFAULT_CSS_PATTERNS = 'snapshot.css';

process.on('SIGINT', () => {
  mainStory.debug('startup', 'CTRL-C received');
  process.exit(0);
});

// ==============================================
// Main
// ==============================================
program
  .version(pkg.version)
  .option('-f --snapshot-patterns [globs]',
    'Glob patterns for snapshot files (comma-separated)',
    DEFAULT_SNAPSHOT_PATTERNS)
  .option('-c --css-patterns [globs]',
    'Glob patterns for CSS stylesheets ' +
    'that will be used for ALL snapshots (comma-separated)',
    DEFAULT_CSS_PATTERNS)
  .option('-p, --port [port]', 'Initial port number to use ' +
    '(if unavailable, the next available one will be used)', Number, DEFAULT_PORT)
  .option('--no-watch', 'Do not watch initially detected snapshot and css files')
  .parse(process.argv);

const cliOptions = program.opts();
cliOptions.snapshotPatterns = cliOptions.snapshotPatterns.split(/\s*,\s*/);
cliOptions.cssPatterns = cliOptions.cssPatterns.split(/\s*,\s*/);

mainStory.info('startup', 'CLI options:', { attach: cliOptions });

let finalPort;
let socketioServer;
const httpInit = () =>
  httpServer.init({ port: cliOptions.port })
  .then((port) => {
    finalPort = port;
    socketioServer = httpServer.getSocketioServer();
  });

const extractorInit = () => {
  extractor.configure({
    snapshotPatterns: cliOptions.snapshotPatterns,
    cssPatterns: cliOptions.cssPatterns,
    watch: cliOptions.watch,
    socketioServer,
  });
  return extractor.start();
};

Promise.resolve()
.then(httpInit)
.then(extractorInit)
.then(() => { opn(`http://localhost:${finalPort}/`); });
<|endoftext|>


<commit_before>goog.provide('ol.control.Rotate');

goog.require('ol.events');
goog.require('ol.events.EventType');
goog.require('ol');
goog.require('ol.control.Control');
goog.require('ol.css');
goog.require('ol.easing');

/**
 * @classdesc
 * A button control to reset rotation to 0.
 * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css
 * selector is added to the button when the rotation is 0.
 *
 * @constructor
 * @extends {ol.control.Control}
 * @param {olx.control.RotateOptions=} opt_options Rotate options.
 * @api
 */
ol.control.Rotate = function(opt_options) {

  var options = opt_options ? opt_options : {};

  var className = options.className !== undefined ? options.className : 'ol-rotate';

  var label = options.label !== undefined ? options.label : '\u21E7';

  /**
   * @type {Element}
   * @private
   */
  this.label_ = null;

  if (typeof label === 'string') {
    this.label_ = document.createElement('span');
    this.label_.className = 'ol-compass';
    this.label_.textContent = label;
  } else {
    this.label_ = label;
    this.label_.classList.add('ol-compass');
  }

  var tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation';

  var button = document.createElement('button');
  button.className = className + '-reset';
  button.setAttribute('type', 'button');
  button.title = tipLabel;
  button.appendChild(this.label_);

  ol.events.listen(button, ol.events.EventType.CLICK,
      ol.control.Rotate.prototype.handleClick_, this);

  var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
      ol.css.CLASS_CONTROL;
  var element = document.createElement('div');
  element.className = cssClasses;
  element.appendChild(button);

  var render = options.render ? options.render : ol.control.Rotate.render;

  this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined;

  ol.control.Control.call(this, {
    element: element,
    render: render,
    target: options.target
  });

  /**
   * @type {number}
   * @private
   */
  this.duration_ = options.duration !== undefined ? options.duration : 250;

  /**
   * @type {boolean}
   * @private
   */
  this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true;

  /**
   * @private
   * @type {number|undefined}
   */
  this.rotation_ = undefined;

  if (this.autoHide_) {
    this.element.classList.add(ol.css.CLASS_HIDDEN);
  }

};
ol.inherits(ol.control.Rotate, ol.control.Control);

/**
 * @param {Event} event The event to handle
 * @private
 */
ol.control.Rotate.prototype.handleClick_ = function(event) {
  event.preventDefault();
  if (this.callResetNorth_ !== undefined) {
    this.callResetNorth_();
  } else {
    this.resetNorth_();
  }
};

/**
 * @private
 */
ol.control.Rotate.prototype.resetNorth_ = function() {
  var map = this.getMap();
  var view = map.getView();
  if (!view) {
    // the map does not have a view, so we can't act
    // upon it
    return;
  }
  var currentRotation = view.getRotation();
  if (currentRotation !== undefined) {
    if (this.duration_ > 0) {
      currentRotation = currentRotation % (2 * Math.PI);
      if (currentRotation < -Math.PI) {
        currentRotation += 2 * Math.PI;
      }
      if (currentRotation > Math.PI) {
        currentRotation -= 2 * Math.PI;
      }
      view.animate({
        rotation: 0,
        duration: this.duration_,
        easing: ol.easing.easeOut
      });
    } else {
      view.setRotation(0);
    }
  }
};

/**
 * Update the rotate control element.
 * @param {ol.MapEvent} mapEvent Map event.
 * @this {ol.control.Rotate}
 * @api
 */
ol.control.Rotate.render = function(mapEvent) {
  var frameState = mapEvent.frameState;
  if (!frameState) {
    return;
  }
  var rotation = frameState.viewState.rotation;
  if (rotation != this.rotation_) {
    var transform = 'rotate(' + rotation + 'rad)';
    if (this.autoHide_) {
      var contains = this.element.classList.contains(ol.css.CLASS_HIDDEN);
      if (!contains && rotation === 0) {
        this.element.classList.add(ol.css.CLASS_HIDDEN);
      } else if (contains && rotation !== 0) {
        this.element.classList.remove(ol.css.CLASS_HIDDEN);
      }
    }
    this.label_.style.msTransform = transform;
    this.label_.style.webkitTransform = transform;
    this.label_.style.transform = transform;
  }
  this.rotation_ = rotation;
};
<commit_msg>Cleanup left over code from animation changes<commit_after>goog.provide('ol.control.Rotate');

goog.require('ol.events');
goog.require('ol.events.EventType');
goog.require('ol');
goog.require('ol.control.Control');
goog.require('ol.css');
goog.require('ol.easing');

/**
 * @classdesc
 * A button control to reset rotation to 0.
 * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css
 * selector is added to the button when the rotation is 0.
 *
 * @constructor
 * @extends {ol.control.Control}
 * @param {olx.control.RotateOptions=} opt_options Rotate options.
 * @api
 */
ol.control.Rotate = function(opt_options) {

  var options = opt_options ? opt_options : {};

  var className = options.className !== undefined ? options.className : 'ol-rotate';

  var label = options.label !== undefined ? options.label : '\u21E7';

  /**
   * @type {Element}
   * @private
   */
  this.label_ = null;

  if (typeof label === 'string') {
    this.label_ = document.createElement('span');
    this.label_.className = 'ol-compass';
    this.label_.textContent = label;
  } else {
    this.label_ = label;
    this.label_.classList.add('ol-compass');
  }

  var tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation';

  var button = document.createElement('button');
  button.className = className + '-reset';
  button.setAttribute('type', 'button');
  button.title = tipLabel;
  button.appendChild(this.label_);

  ol.events.listen(button, ol.events.EventType.CLICK,
      ol.control.Rotate.prototype.handleClick_, this);

  var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
      ol.css.CLASS_CONTROL;
  var element = document.createElement('div');
  element.className = cssClasses;
  element.appendChild(button);

  var render = options.render ? options.render : ol.control.Rotate.render;

  this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined;

  ol.control.Control.call(this, {
    element: element,
    render: render,
    target: options.target
  });

  /**
   * @type {number}
   * @private
   */
  this.duration_ = options.duration !== undefined ? options.duration : 250;

  /**
   * @type {boolean}
   * @private
   */
  this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true;

  /**
   * @private
   * @type {number|undefined}
   */
  this.rotation_ = undefined;

  if (this.autoHide_) {
    this.element.classList.add(ol.css.CLASS_HIDDEN);
  }

};
ol.inherits(ol.control.Rotate, ol.control.Control);

/**
 * @param {Event} event The event to handle
 * @private
 */
ol.control.Rotate.prototype.handleClick_ = function(event) {
  event.preventDefault();
  if (this.callResetNorth_ !== undefined) {
    this.callResetNorth_();
  } else {
    this.resetNorth_();
  }
};

/**
 * @private
 */
ol.control.Rotate.prototype.resetNorth_ = function() {
  var map = this.getMap();
  var view = map.getView();
  if (!view) {
    // the map does not have a view, so we can't act
    // upon it
    return;
  }
  if (view.getRotation() !== undefined) {
    if (this.duration_ > 0) {
      view.animate({
        rotation: 0,
        duration: this.duration_,
        easing: ol.easing.easeOut
      });
    } else {
      view.setRotation(0);
    }
  }
};

/**
 * Update the rotate control element.
 * @param {ol.MapEvent} mapEvent Map event.
 * @this {ol.control.Rotate}
 * @api
 */
ol.control.Rotate.render = function(mapEvent) {
  var frameState = mapEvent.frameState;
  if (!frameState) {
    return;
  }
  var rotation = frameState.viewState.rotation;
  if (rotation != this.rotation_) {
    var transform = 'rotate(' + rotation + 'rad)';
    if (this.autoHide_) {
      var contains = this.element.classList.contains(ol.css.CLASS_HIDDEN);
      if (!contains && rotation === 0) {
        this.element.classList.add(ol.css.CLASS_HIDDEN);
      } else if (contains && rotation !== 0) {
        this.element.classList.remove(ol.css.CLASS_HIDDEN);
      }
    }
    this.label_.style.msTransform = transform;
    this.label_.style.webkitTransform = transform;
    this.label_.style.transform = transform;
  }
  this.rotation_ = rotation;
};
<|endoftext|>


<commit_before>

var _expectRequiredException = function(test, defaultsMap) {

  test.throws(
    function () {
      MeteorSettings.setDefaults( defaultsMap, MeteorSettings.REQUIRED );
    },
    'settings-required'
  )
}

Tinytest.add("Not required, no Meteor.settings", function (test) {

  Meteor.settings = null;

  MeteorSettings.setDefaults( {} );
});

Tinytest.add("Required, no Meteor.settings", function (test) {

  Meteor.settings = null;

  _expectRequiredException( test, {} );
});

Tinytest.add("Required, no Meteor.settings, with defaults", function (test) {

  Meteor.settings = null;

  _expectRequiredException( test, { public: 1 } );
});

Tinytest.add("Simple defaults, null Meteor.settings", function (test) {

  Meteor.settings = null;

  var simpleDefaults = { serverValue: 1 };

  MeteorSettings.setDefaults( simpleDefaults );

  var expected = { serverValue: 1, public: {} };

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Simple defaults, empty Meteor.settings", function (test) {

  Meteor.settings = {};

  var simpleDefaults = { serverValue: 1 };

  MeteorSettings.setDefaults( simpleDefaults );

  var expected = { serverValue: 1, public: {} };

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Simple public, null Meteor.settings", function (test) {

  Meteor.settings = null;

  var simpleDefaults = { public: { value: 1 } };

  MeteorSettings.setDefaults( simpleDefaults );

  test.equal( Meteor.settings, simpleDefaults );
});

Tinytest.add("Simple public, empty Meteor.settings", function (test) {

  Meteor.settings = {};

  var simpleDefaults = { public: { value: 1 } };

  MeteorSettings.setDefaults( simpleDefaults );

  test.equal( Meteor.settings, simpleDefaults );
});

Tinytest.add("Merge public with public", function (test) {

  Meteor.settings = { public: { value: 10 } };

  var defaults = {
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    public: { value: 10, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge public with different public", function (test) {

  Meteor.settings = { public: { value: 2 } };

  var defaults = {
    public: { other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    public: { value: 2, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge public with public and server", function (test) {

  Meteor.settings = { public: { value: 10 } };

  var defaults = {
    serverValue: 1,
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    serverValue: 1,
    public: { value: 10, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge empty settings with public", function (test) {

  Meteor.settings = { };

  var defaults = {
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = defaults;

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge server with public", function (test) {

  Meteor.settings = { serverValue: 1 };

  var defaults = {
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    serverValue: 1,
    public: { value: 2, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});
<commit_msg>Instructions to run tests<commit_after>var _expectRequiredException = function(test, defaultsMap) {

  test.throws(
    function () {
      MeteorSettings.setDefaults( defaultsMap, MeteorSettings.REQUIRED );
    },
    'settings-required'
  )
}

Tinytest.add("Not required, no Meteor.settings", function (test) {

  Meteor.settings = null;

  MeteorSettings.setDefaults( {} );
});

Tinytest.add("Required, no Meteor.settings", function (test) {

  Meteor.settings = null;

  _expectRequiredException( test, {} );
});

Tinytest.add("Required, no Meteor.settings, with defaults", function (test) {

  Meteor.settings = null;

  _expectRequiredException( test, { public: 1 } );
});

Tinytest.add("Simple defaults, null Meteor.settings", function (test) {

  Meteor.settings = null;

  var simpleDefaults = { serverValue: 1 };

  MeteorSettings.setDefaults( simpleDefaults );

  var expected = { serverValue: 1, public: {} };

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Simple defaults, empty Meteor.settings", function (test) {

  Meteor.settings = {};

  var simpleDefaults = { serverValue: 1 };

  MeteorSettings.setDefaults( simpleDefaults );

  var expected = { serverValue: 1, public: {} };

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Simple public, null Meteor.settings", function (test) {

  Meteor.settings = null;

  var simpleDefaults = { public: { value: 1 } };

  MeteorSettings.setDefaults( simpleDefaults );

  test.equal( Meteor.settings, simpleDefaults );
});

Tinytest.add("Simple public, empty Meteor.settings", function (test) {

  Meteor.settings = {};

  var simpleDefaults = { public: { value: 1 } };

  MeteorSettings.setDefaults( simpleDefaults );

  test.equal( Meteor.settings, simpleDefaults );
});

Tinytest.add("Merge public with public", function (test) {

  Meteor.settings = { public: { value: 10 } };

  var defaults = {
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    public: { value: 10, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge public with different public", function (test) {

  Meteor.settings = { public: { value: 2 } };

  var defaults = {
    public: { other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    public: { value: 2, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge public with public and server", function (test) {

  Meteor.settings = { public: { value: 10 } };

  var defaults = {
    serverValue: 1,
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    serverValue: 1,
    public: { value: 10, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge empty settings with public", function (test) {

  Meteor.settings = { };

  var defaults = {
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = defaults;

  test.equal( Meteor.settings, expected );
});

Tinytest.add("Merge server with public", function (test) {

  Meteor.settings = { serverValue: 1 };

  var defaults = {
    public: { value: 2, other: 3 }
  };

  MeteorSettings.setDefaults( defaults );

  var expected = {
    serverValue: 1,
    public: { value: 2, other: 3 }
  }

  test.equal( Meteor.settings, expected );
});
<|endoftext|>