<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('bundle-import-helpers', function(Y) {
  var ns = Y.namespace('juju');

  ns.BundleImport = {
    /**
      Calls the deployer import method with the bundle data
      to deploy the bundle to the environment.

      @method deployBundle
      @param {Object} bundle Bundle data.
      @param {Environment} env Environment with access to the bundle back end.
      calls.
      @param {Database} db The app Database with access to the NotificationList.
    */
    deployBundle: function(bundle, env, db, customCallback) {
      var notifications = db.notifications;

      var defaultCallback = function(result) {
        if (result.err) {
          console.log('bundle import failed:', result.err);
          notifications.add({
            title: 'Bundle deployment failed',
            message: 'Unable to deploy the bundle. The server returned the ' +
                'following error: ' + result.err,
            level: 'error'
          });
        } else {
          notifications.add({
            title: 'Bundle deployment requested',
            message: 'Bundle deployment request successful. The full ' +
                'deployment can take some time to complete',
            level: 'important'
          });

          ns.BundleImport._watchDeployment(result.DeploymentId, env, db);
        }
      };

      if (Y.Lang.isFunction(env.deployerImport)) {
        env.deployerImport(
            bundle,
            null,
            customCallback ? customCallback : defaultCallback
        );
      } else {
        notifications.add({
          title: 'Bundle deployment not available.',
          message: 'Bundle deployments are not currently available for your' +
              'environment.',
          level: 'error'
        });
      }
    },

    /**
      Deploy one or more files, generally from the import file picker
      utility.

      @method deployBundleFiles
      @param {Array} fileSources The list of files from the import control.
      @param {Environment} env Environment with access to the bundle back end.
      calls.
      @param {Database} db The app Database with access to the NotificationList.
     */
    deployBundleFiles: function(fileSources, env, db) {
      var notifications = db.notifications;
      if (!Y.Lang.isFunction(env.deployerImport)) {
        // Notify the user that their environment is too old and return.
        notifications.add({
          title: 'Deployer Import Unsupported',
          message: 'Your environment is too old to support deployer file' +
              ' imports directly. Please consider upgrading to use' +
              ' this feature.',
          level: 'important'
        });
        return;
      }

      // Handle dropping Deployer files on the canvas.
      Y.Array.each(fileSources, function(file) {
        var reader = new FileReader();

        // Once the file has been loaded, deploy it.
        reader.onload = function(e) {
          ns.BundleImport.deployBundle(
              e.target.result,
              env,
              db,
              function(result) {
                if (!result.err) {

                  notifications.add({
                    title: 'Imported Deployer file',
                    message: 'Import from "' + file.name +
                        '" successful. This ' +
                        'can take some time to complete.',
                    level: 'important'
                  });

                } else {
                  console.warn('import failed', file, result);
                  notifications.add({
                    title: 'Import Environment Failed',
                    message: 'Import from "' + file.name +
                        '" failed.
' + result.err,
                    level: 'error'
                  });

                  ns.BundleImport._watchDeployment(
                      result.DeploymentId, env, db);
                }
              }
          );
        };

        // Start the loading process.
        reader.readAsText(file);
      });
    },

    /**
      Watch a deployment for changes and notify the user of updates.

      @method watchDeployment
      @param {Integer} deploymentId The ID returned from the deployment call.
      @param {Environment} env The environment so that we can call the back
      end.
      @param {Database} db The db which contains the NotificationList used
      for adding notifications to the system.
     */
    _watchDeployment: function(deploymentId, env, db) {
      var notifications = db.notifications;

      // First generate a watch.
      env.deployerWatch(deploymentId, function(data) {
        if (data.err) {
          notifications.add({
            title: 'Unable to watch status of import.',
            message: 'Attempting to watch the deployment failed: ' +
                data.err,
            level: 'error'
          });
        } else {
          ns.BundleImport._processWatchDeploymentUpdates(
              data.WatchId, env, db);
        }
      });
    },

    /**
      Once we've got a Watch, we need to continue to call Next on it to
      receive updates. Watch the deployment until it's either completed or
      we've gotten an error.

      This will make nested calls upon each update from the server and could
      be a potential point for recursive callback issues. It appears that we
      should only get a couple of levels deep in the notification stack and
      will be safe.

      @method watchDeployment
      @param {Integer} watchId The ID of the Watcher from the watchDeployment
      call.
      @param {Environment} env The environment so that we can call the
      back end.
      @param {Database} db The db which contains the NotificationList used
      for adding notifications to the system.
     */
    _processWatchDeploymentUpdates: function(watchId, env, db) {
      var notifications = db.notifications;

      var processUpdate = function(data) {
        if (data.err) {
          notifications.add({
            title: 'Error watching deployment',
            message: 'The watch of the deployment errored:' +
                data.err,
            level: 'error'
          });

          // Break the loop of calling for the next update from the server.
          return;

        } else {
          // Just grab the latest change and notify the user of the
          // status.
          var newChange = data.Changes[0];
          // The change could be the result of an error.
          if (newChange.Error) {
            notifications.add({
              title: 'Updated status for deployment: ' +
                  newChange.DeploymentId,
              message: 'The deployment errored: ' +
                  newChange.Error,
              level: 'error'
            });
          } else {
            notifications.add({
              title: 'Updated status for deployment: ' +
                  newChange.DeploymentId,
              message: 'The deployment is currently: ' +
                  newChange.Status,
              level: 'important'
            });
          }

          // If the status is 'completed' then we're done watching this.
          if (newChange.Status === 'completed') {
            // There's nothing else to see here.
            return;
          } else {
            env.deployerWatchUpdate(watchId, processUpdate);
          }
        }
      };

      // Make the first call to the env and the processUpdate callback will
      // handle re-calling on each update.
      env.deployerWatchUpdate(watchId, processUpdate);
    }

  };

}, '0.1.0', {
  requires: []
});
<commit_msg>Move the watch to the right section<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('bundle-import-helpers', function(Y) {
  var ns = Y.namespace('juju');

  ns.BundleImport = {
    /**
      Calls the deployer import method with the bundle data
      to deploy the bundle to the environment.

      @method deployBundle
      @param {Object} bundle Bundle data.
      @param {Environment} env Environment with access to the bundle back end.
      calls.
      @param {Database} db The app Database with access to the NotificationList.
    */
    deployBundle: function(bundle, env, db, customCallback) {
      var notifications = db.notifications;

      var defaultCallback = function(result) {
        if (result.err) {
          console.log('bundle import failed:', result.err);
          notifications.add({
            title: 'Bundle deployment failed',
            message: 'Unable to deploy the bundle. The server returned the ' +
                'following error: ' + result.err,
            level: 'error'
          });
        } else {
          notifications.add({
            title: 'Bundle deployment requested',
            message: 'Bundle deployment request successful. The full ' +
                'deployment can take some time to complete',
            level: 'important'
          });

          ns.BundleImport._watchDeployment(result.DeploymentId, env, db);
        }
      };

      if (Y.Lang.isFunction(env.deployerImport)) {
        env.deployerImport(
            bundle,
            null,
            customCallback ? customCallback : defaultCallback
        );
      } else {
        notifications.add({
          title: 'Bundle deployment not available.',
          message: 'Bundle deployments are not currently available for your' +
              'environment.',
          level: 'error'
        });
      }
    },

    /**
      Deploy one or more files, generally from the import file picker
      utility.

      @method deployBundleFiles
      @param {Array} fileSources The list of files from the import control.
      @param {Environment} env Environment with access to the bundle back end.
      calls.
      @param {Database} db The app Database with access to the NotificationList.
     */
    deployBundleFiles: function(fileSources, env, db) {
      var notifications = db.notifications;
      if (!Y.Lang.isFunction(env.deployerImport)) {
        // Notify the user that their environment is too old and return.
        notifications.add({
          title: 'Deployer Import Unsupported',
          message: 'Your environment is too old to support deployer file' +
              ' imports directly. Please consider upgrading to use' +
              ' this feature.',
          level: 'important'
        });
        return;
      }

      // Handle dropping Deployer files on the canvas.
      Y.Array.each(fileSources, function(file) {
        var reader = new FileReader();

        // Once the file has been loaded, deploy it.
        reader.onload = function(e) {
          ns.BundleImport.deployBundle(
              e.target.result,
              env,
              db,
              function(result) {
                if (!result.err) {

                  notifications.add({
                    title: 'Imported Deployer file',
                    message: 'Import from "' + file.name +
                        '" successful. This ' +
                        'can take some time to complete.',
                    level: 'important'
                  });

                  ns.BundleImport._watchDeployment(
                      result.DeploymentId, env, db);

                } else {
                  console.warn('import failed', file, result);
                  notifications.add({
                    title: 'Import Environment Failed',
                    message: 'Import from "' + file.name +
                        '" failed.
' + result.err,
                    level: 'error'
                  });
                }
              }
          );
        };

        // Start the loading process.
        reader.readAsText(file);
      });
    },

    /**
      Watch a deployment for changes and notify the user of updates.

      @method watchDeployment
      @param {Integer} deploymentId The ID returned from the deployment call.
      @param {Environment} env The environment so that we can call the back
      end.
      @param {Database} db The db which contains the NotificationList used
      for adding notifications to the system.
     */
    _watchDeployment: function(deploymentId, env, db) {
      var notifications = db.notifications;

      // First generate a watch.
      env.deployerWatch(deploymentId, function(data) {
        if (data.err) {
          notifications.add({
            title: 'Unable to watch status of import.',
            message: 'Attempting to watch the deployment failed: ' +
                data.err,
            level: 'error'
          });
        } else {
          ns.BundleImport._processWatchDeploymentUpdates(
              data.WatchId, env, db);
        }
      });
    },

    /**
      Once we've got a Watch, we need to continue to call Next on it to
      receive updates. Watch the deployment until it's either completed or
      we've gotten an error.

      This will make nested calls upon each update from the server and could
      be a potential point for recursive callback issues. It appears that we
      should only get a couple of levels deep in the notification stack and
      will be safe.

      @method watchDeployment
      @param {Integer} watchId The ID of the Watcher from the watchDeployment
      call.
      @param {Environment} env The environment so that we can call the
      back end.
      @param {Database} db The db which contains the NotificationList used
      for adding notifications to the system.
     */
    _processWatchDeploymentUpdates: function(watchId, env, db) {
      var notifications = db.notifications;

      var processUpdate = function(data) {
        if (data.err) {
          notifications.add({
            title: 'Error watching deployment',
            message: 'The watch of the deployment errored:' +
                data.err,
            level: 'error'
          });

          // Break the loop of calling for the next update from the server.
          return;

        } else {
          // Just grab the latest change and notify the user of the
          // status.
          var newChange = data.Changes[0];
          // The change could be the result of an error.
          if (newChange.Error) {
            notifications.add({
              title: 'Updated status for deployment: ' +
                  newChange.DeploymentId,
              message: 'The deployment errored: ' +
                  newChange.Error,
              level: 'error'
            });
          } else {
            notifications.add({
              title: 'Updated status for deployment: ' +
                  newChange.DeploymentId,
              message: 'The deployment is currently: ' +
                  newChange.Status,
              level: 'important'
            });
          }

          // If the status is 'completed' then we're done watching this.
          if (newChange.Status === 'completed') {
            // There's nothing else to see here.
            return;
          } else {
            env.deployerWatchUpdate(watchId, processUpdate);
          }
        }
      };

      // Make the first call to the env and the processUpdate callback will
      // handle re-calling on each update.
      env.deployerWatchUpdate(watchId, processUpdate);
    }

  };

}, '0.1.0', {
  requires: []
});
<|endoftext|>


<commit_before>'use babel'

import {CompositeDisposable} from 'atom'
import {obtainConfig} from './helpers'

export default class Flint {
  constructor() {
    this.subscriptions = new CompositeDisposable()
    this.config = new Map()
  }
  activate() {
    let pathsQueue = Promise.resolve()
    this.subscriptions.add(atom.project.onDidChangePaths(paths => {
      pathsQueue.then(() => this.handlePaths(paths))
    }))
    this.handlePaths(atom.project.getPaths())
  }
  handlePaths(paths) {
    const promises = []

    // Add new ones
    for (const newPath of paths) {
      if (!this.config.has(newPath)) {
        promises.push(obtainConfig(newPath))
      }
    }

    // Remove deleted ones
    for (const oldPath of this.config.keys()) {
      if (paths.indexOf(oldPath) === -1) {
        this.config.delete(oldPath)
      }
    }

    return Promise.all(promises).then(configs => {
      for (const config of configs) {
        if (config === null) {
          continue
        }
        this.config.set(config.path, config.manifest)
      }
    })
  }
  dispose() {
    this.config.clear()
    this.subscriptions.dispose()
  }
}
<commit_msg>:new: Add some basic events<commit_after>'use babel'

import {CompositeDisposable, Emitter} from 'atom'
import {obtainConfig} from './helpers'

export default class Flint {
  constructor() {
    this.subscriptions = new CompositeDisposable()
    this.emitter = new Emitter()
    this.config = new Map()

    this.subscriptions.add(this.emitter)
  }
  activate() {
    let pathsQueue = Promise.resolve()
    this.subscriptions.add(atom.project.onDidChangePaths(paths => {
      pathsQueue.then(() => this.handlePaths(paths))
    }))
    this.handlePaths(atom.project.getPaths())
  }
  handlePaths(paths) {
    const promises = []

    // Add new ones
    for (const newPath of paths) {
      if (!this.config.has(newPath)) {
        promises.push(obtainConfig(newPath))
      }
    }

    // Remove deleted ones
    for (const oldPath of this.config.keys()) {
      if (paths.indexOf(oldPath) === -1) {
        this.config.delete(oldPath)
        this.emitter.emit('did-remove-project', oldPath)
      }
    }

    return Promise.all(promises).then(configs => {
      for (const config of configs) {
        if (config === null) {
          continue
        }
        this.config.set(config.path, config.manifest)
        this.emitter.emit('did-add-project', {config})
      }
    })
  }
  onDidAddProject(callback) {
    return this.emitter.on('did-add-project', callback)
  }
  onDidRemoveProject(callback) {
    return this.emitter.on('did-remove-project', callback)
  }
  dispose() {
    this.config.clear()
    this.subscriptions.dispose()
  }
}
<|endoftext|>


<commit_before>"use strict"

import { Emitter, CompositeDisposable } from "atom"
import MinimapElement from "./minimap-element"
import Minimap from "./minimap"
import config from "./config.json"
import * as PluginManagement from "./plugin-management"
import { treeSitterWarning } from "./performance-monitor"
import DOMStylesReader from "./dom-styles-reader"

export { default as config } from "./config.json"
export * from "./plugin-management"
export { default as Minimap } from "./minimap"
export { default as MinimapElement } from "./minimap-element"

/**
 * The `Minimap` package provides an eagle-eye view of text buffers.
 *
 * It also provides API for plugin packages that want to interact with the
 * minimap and be available to the user through the minimap settings.
 */

/**
 * The activation state of the package.
 *
 * @type {boolean}
 * @access private
 */
let active = false
/**
 * The toggle state of the package.
 *
 * @type {boolean}
 * @access private
 */
let toggled = false
/**
 * The `Map` where Minimap instances are stored with the text editor they
 * target as key.
 *
 * @type {Map}
 * @access private
 */
export let editorsMinimaps = null
/**
 * The composite disposable that stores the package's subscriptions.
 *
 * @type {CompositeDisposable}
 * @access private
 */
let subscriptions = null
/**
 * The disposable that stores the package's commands subscription.
 *
 * @type {Disposable}
 * @access private
 */
let subscriptionsOfCommands = null

/**
 * The package's events emitter.
 *
 * @type {Emitter}
 * @access private
 */
export const emitter = new Emitter()

/**
  DOMStylesReader cache used for storing token colors
*/
export let domStylesReader = null

/**
 * Activates the minimap package.
 */
export function activate() {
  if (active) {
    return
  }

  subscriptionsOfCommands = atom.commands.add("atom-workspace", {
    "minimap:toggle": () => {
      toggle()
    },
    "minimap:generate-coffee-plugin": async () => {
      await generatePlugin("coffee")
    },
    "minimap:generate-javascript-plugin": async () => {
      await generatePlugin("javascript")
    },
    "minimap:generate-babel-plugin": async () => {
      await generatePlugin("babel")
    },
  })

  editorsMinimaps = new Map()
  domStylesReader = new DOMStylesReader()

  subscriptions = new CompositeDisposable()
  active = true

  if (atom.config.get("minimap.autoToggle")) {
    toggle()
  }
}

/**
 * Returns a {MinimapElement} for the passed-in model if it's a {Minimap}.
 *
 * @param {Minimap} model the model for which returning a view
 * @return {MinimapElement}
 */
export function minimapViewProvider(model) {
  if (model instanceof Minimap) {
    let element = model.getMinimapElement()
    if (!element) {
      element = new MinimapElement()
      element.setModel(model)
    }
    return element
  }
}

/**
 * Deactivates the minimap package.
 */
export function deactivate() {
  if (!active) {
    return
  }

  PluginManagement.deactivateAllPlugins()

  if (editorsMinimaps) {
    editorsMinimaps.forEach((value) => {
      value.destroy()
    })
    editorsMinimaps.clear()
  }

  subscriptions.dispose()
  subscriptionsOfCommands.dispose()
  domStylesReader.invalidateDOMStylesCache()
  toggled = false
  active = false
}

export function getConfigSchema() {
  return config || atom.packages.getLoadedPackage("minimap").metadata.configSchema
}

/**
 * Toggles the minimap display.
 */
export function toggle() {
  if (!active) {
    return
  }

  if (toggled) {
    toggled = false

    if (editorsMinimaps) {
      editorsMinimaps.forEach((minimap) => {
        minimap.destroy()
      })
      editorsMinimaps.clear()
    }
    subscriptions.dispose()

    // HACK: this hack forces rerendering editor size which moves the scrollbar to the right once minimap is removed
    const wasMaximized = atom.isMaximized()
    const { width, height } = atom.getSize()
    atom.setSize(width, height)
    if (wasMaximized) {
      atom.maximize()
    }
  } else {
    toggled = true
    initSubscriptions()
  }
  domStylesReader.invalidateDOMStylesCache()
}

/**
 * Opens the plugin generation view.
 *
 * @param  {string} template the name of the template to use
 */
async function generatePlugin(template) {
  const { default: MinimapPluginGeneratorElement } = await import("./minimap-plugin-generator-element")
  const view = new MinimapPluginGeneratorElement()
  view.template = template
  view.attach()
}

/**
 * Registers a callback to listen to the `did-activate` event of the package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidActivate(callback) {
  return emitter.on("did-activate", callback)
}

/**
 * Registers a callback to listen to the `did-deactivate` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidDeactivate(callback) {
  return emitter.on("did-deactivate", callback)
}

/**
 * Registers a callback to listen to the `did-create-minimap` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidCreateMinimap(callback) {
  return emitter.on("did-create-minimap", callback)
}

/**
 * Registers a callback to listen to the `did-add-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidAddPlugin(callback) {
  return emitter.on("did-add-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-remove-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidRemovePlugin(callback) {
  return emitter.on("did-remove-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-activate-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidActivatePlugin(callback) {
  return emitter.on("did-activate-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-deactivate-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidDeactivatePlugin(callback) {
  return emitter.on("did-deactivate-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-change-plugin-order` event of
 * the package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidChangePluginOrder(callback) {
  return emitter.on("did-change-plugin-order", callback)
}

/**
 * Returns the `Minimap` class
 *
 * @return {Function} the `Minimap` class constructor
 */
export function minimapClass() {
  return Minimap
}

/**
 * Returns the `Minimap` object associated to the passed-in
 * `TextEditorElement`.
 *
 * @param  {TextEditorElement} editorElement a text editor element
 * @return {Minimap} the associated minimap
 */
export function minimapForEditorElement(editorElement) {
  if (!editorElement) {
    return
  }
  return minimapForEditor(editorElement.getModel())
}

/**
 * Returns the `Minimap` object associated to the passed-in
 * `TextEditor`.
 *
 * @param  {TextEditor} textEditor a text editor
 * @return {Minimap} the associated minimap
 */
export function minimapForEditor(textEditor) {
  if (!textEditor) {
    return
  }
  if (!editorsMinimaps) {
    return
  }

  let minimap = editorsMinimaps.get(textEditor)

  if (minimap === undefined || minimap.destroyed) {
    minimap = new Minimap({ textEditor })
    editorsMinimaps.set(textEditor, minimap)

    const editorSubscription = textEditor.onDidDestroy(() => {
      if (editorsMinimaps) {
        editorsMinimaps.delete(textEditor)
      }
      if (minimap) {
        // just in case
        minimap.destroy()
      }
      editorSubscription.dispose()
    })
    // dispose the editorSubscription if minimap is deactivated before destroying the editor
    subscriptions.add(editorSubscription)
  }

  return minimap
}

/**
 * Returns a new stand-alone {Minimap} for the passed-in `TextEditor`.
 *
 * @param  {TextEditor} textEditor a text editor instance to create
 *                                 a minimap for
 * @return {Minimap} a new stand-alone Minimap for the passed-in editor
 */
export function standAloneMinimapForEditor(textEditor) {
  if (!textEditor) {
    return
  }

  return new Minimap({
    textEditor,
    standAlone: true,
  })
}

/**
 * Returns the `Minimap` associated to the active `TextEditor`.
 *
 * @return {Minimap} the active Minimap
 */
export function getActiveMinimap() {
  return minimapForEditor(atom.workspace.getActiveTextEditor())
}

/**
 * Calls a function for each present and future minimaps.
 *
 * @param  {function(minimap:Minimap):void} iterator a function to call with
 *                                                   the existing and future
 *                                                   minimaps
 * @return {Disposable} a disposable to unregister the observer
 */
export function observeMinimaps(iterator) {
  if (!iterator) {
    return
  }

  if (editorsMinimaps) {
    editorsMinimaps.forEach((minimap) => {
      iterator(minimap)
    })
  }
  return onDidCreateMinimap((minimap) => {
    iterator(minimap)
  })
}

/**
 * Registers to the `observeTextEditors` method.
 *
 * @access private
 */
function initSubscriptions() {
  subscriptions.add(
    atom.workspace.observeTextEditors((textEditor) => {
      const minimap = minimapForEditor(textEditor)
      const minimapElement = minimapViewProvider(minimap)

      emitter.emit("did-create-minimap", minimap)
      minimapElement.attach(textEditor.getElement())
    }),
    // empty color cache if the theme changes
    atom.themes.onDidChangeActiveThemes(updateStyles),
    atom.styles.onDidUpdateStyleElement(updateStyles),
    atom.styles.onDidAddStyleElement(updateStyles),
    atom.styles.onDidRemoveStyleElement(updateStyles),
    treeSitterWarning()
  )
}

function updateStyles() {
  domStylesReader.invalidateDOMStylesCache()
  editorsMinimaps.forEach((minimap) => {
    atom.views.getView(minimap).requestForcedUpdate()
  })
}

// The public exports included in the service:
const MinimapServiceV1 = {
  minimapViewProvider,
  getConfigSchema,
  onDidActivate,
  onDidDeactivate,
  onDidCreateMinimap,
  onDidAddPlugin,
  onDidRemovePlugin,
  onDidActivatePlugin,
  onDidDeactivatePlugin,
  onDidChangePluginOrder,
  minimapClass,
  minimapForEditorElement,
  minimapForEditor,
  standAloneMinimapForEditor,
  getActiveMinimap,
  observeMinimaps,
  registerPlugin: PluginManagement.registerPlugin,
  unregisterPlugin: PluginManagement.unregisterPlugin,
  togglePluginActivation: PluginManagement.togglePluginActivation,
  deactivateAllPlugins: PluginManagement.deactivateAllPlugins,
  activatePlugin: PluginManagement.activatePlugin,
  deactivatePlugin: PluginManagement.deactivatePlugin,
  getPluginsOrder: PluginManagement.getPluginsOrder,
}

/**
 * Returns the Minimap main module instance.
 *
 * @return {Main} The Minimap main module instance.
 */
export function provideMinimapServiceV1() {
  return MinimapServiceV1
}
<commit_msg>chore: debounce update styles<commit_after>"use strict"

import { Emitter, CompositeDisposable } from "atom"
import MinimapElement from "./minimap-element"
import Minimap from "./minimap"
import config from "./config.json"
import * as PluginManagement from "./plugin-management"
import { treeSitterWarning } from "./performance-monitor"
import DOMStylesReader from "./dom-styles-reader"
import { debounce } from "underscore-plus"

export { default as config } from "./config.json"
export * from "./plugin-management"
export { default as Minimap } from "./minimap"
export { default as MinimapElement } from "./minimap-element"

/**
 * The `Minimap` package provides an eagle-eye view of text buffers.
 *
 * It also provides API for plugin packages that want to interact with the
 * minimap and be available to the user through the minimap settings.
 */

/**
 * The activation state of the package.
 *
 * @type {boolean}
 * @access private
 */
let active = false
/**
 * The toggle state of the package.
 *
 * @type {boolean}
 * @access private
 */
let toggled = false
/**
 * The `Map` where Minimap instances are stored with the text editor they
 * target as key.
 *
 * @type {Map}
 * @access private
 */
export let editorsMinimaps = null
/**
 * The composite disposable that stores the package's subscriptions.
 *
 * @type {CompositeDisposable}
 * @access private
 */
let subscriptions = null
/**
 * The disposable that stores the package's commands subscription.
 *
 * @type {Disposable}
 * @access private
 */
let subscriptionsOfCommands = null

/**
 * The package's events emitter.
 *
 * @type {Emitter}
 * @access private
 */
export const emitter = new Emitter()

/**
  DOMStylesReader cache used for storing token colors
*/
export let domStylesReader = null

/**
 * Activates the minimap package.
 */
export function activate() {
  if (active) {
    return
  }

  subscriptionsOfCommands = atom.commands.add("atom-workspace", {
    "minimap:toggle": () => {
      toggle()
    },
    "minimap:generate-coffee-plugin": async () => {
      await generatePlugin("coffee")
    },
    "minimap:generate-javascript-plugin": async () => {
      await generatePlugin("javascript")
    },
    "minimap:generate-babel-plugin": async () => {
      await generatePlugin("babel")
    },
  })

  editorsMinimaps = new Map()
  domStylesReader = new DOMStylesReader()

  subscriptions = new CompositeDisposable()
  active = true

  if (atom.config.get("minimap.autoToggle")) {
    toggle()
  }
}

/**
 * Returns a {MinimapElement} for the passed-in model if it's a {Minimap}.
 *
 * @param {Minimap} model the model for which returning a view
 * @return {MinimapElement}
 */
export function minimapViewProvider(model) {
  if (model instanceof Minimap) {
    let element = model.getMinimapElement()
    if (!element) {
      element = new MinimapElement()
      element.setModel(model)
    }
    return element
  }
}

/**
 * Deactivates the minimap package.
 */
export function deactivate() {
  if (!active) {
    return
  }

  PluginManagement.deactivateAllPlugins()

  if (editorsMinimaps) {
    editorsMinimaps.forEach((value) => {
      value.destroy()
    })
    editorsMinimaps.clear()
  }

  subscriptions.dispose()
  subscriptionsOfCommands.dispose()
  domStylesReader.invalidateDOMStylesCache()
  toggled = false
  active = false
}

export function getConfigSchema() {
  return config || atom.packages.getLoadedPackage("minimap").metadata.configSchema
}

/**
 * Toggles the minimap display.
 */
export function toggle() {
  if (!active) {
    return
  }

  if (toggled) {
    toggled = false

    if (editorsMinimaps) {
      editorsMinimaps.forEach((minimap) => {
        minimap.destroy()
      })
      editorsMinimaps.clear()
    }
    subscriptions.dispose()

    // HACK: this hack forces rerendering editor size which moves the scrollbar to the right once minimap is removed
    const wasMaximized = atom.isMaximized()
    const { width, height } = atom.getSize()
    atom.setSize(width, height)
    if (wasMaximized) {
      atom.maximize()
    }
  } else {
    toggled = true
    initSubscriptions()
  }
  domStylesReader.invalidateDOMStylesCache()
}

/**
 * Opens the plugin generation view.
 *
 * @param  {string} template the name of the template to use
 */
async function generatePlugin(template) {
  const { default: MinimapPluginGeneratorElement } = await import("./minimap-plugin-generator-element")
  const view = new MinimapPluginGeneratorElement()
  view.template = template
  view.attach()
}

/**
 * Registers a callback to listen to the `did-activate` event of the package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidActivate(callback) {
  return emitter.on("did-activate", callback)
}

/**
 * Registers a callback to listen to the `did-deactivate` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidDeactivate(callback) {
  return emitter.on("did-deactivate", callback)
}

/**
 * Registers a callback to listen to the `did-create-minimap` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidCreateMinimap(callback) {
  return emitter.on("did-create-minimap", callback)
}

/**
 * Registers a callback to listen to the `did-add-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidAddPlugin(callback) {
  return emitter.on("did-add-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-remove-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidRemovePlugin(callback) {
  return emitter.on("did-remove-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-activate-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidActivatePlugin(callback) {
  return emitter.on("did-activate-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-deactivate-plugin` event of the
 * package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidDeactivatePlugin(callback) {
  return emitter.on("did-deactivate-plugin", callback)
}

/**
 * Registers a callback to listen to the `did-change-plugin-order` event of
 * the package.
 *
 * @param  {function(event:Object):void} callback the callback function
 * @return {Disposable} a disposable to stop listening to the event
 */
export function onDidChangePluginOrder(callback) {
  return emitter.on("did-change-plugin-order", callback)
}

/**
 * Returns the `Minimap` class
 *
 * @return {Function} the `Minimap` class constructor
 */
export function minimapClass() {
  return Minimap
}

/**
 * Returns the `Minimap` object associated to the passed-in
 * `TextEditorElement`.
 *
 * @param  {TextEditorElement} editorElement a text editor element
 * @return {Minimap} the associated minimap
 */
export function minimapForEditorElement(editorElement) {
  if (!editorElement) {
    return
  }
  return minimapForEditor(editorElement.getModel())
}

/**
 * Returns the `Minimap` object associated to the passed-in
 * `TextEditor`.
 *
 * @param  {TextEditor} textEditor a text editor
 * @return {Minimap} the associated minimap
 */
export function minimapForEditor(textEditor) {
  if (!textEditor) {
    return
  }
  if (!editorsMinimaps) {
    return
  }

  let minimap = editorsMinimaps.get(textEditor)

  if (minimap === undefined || minimap.destroyed) {
    minimap = new Minimap({ textEditor })
    editorsMinimaps.set(textEditor, minimap)

    const editorSubscription = textEditor.onDidDestroy(() => {
      if (editorsMinimaps) {
        editorsMinimaps.delete(textEditor)
      }
      if (minimap) {
        // just in case
        minimap.destroy()
      }
      editorSubscription.dispose()
    })
    // dispose the editorSubscription if minimap is deactivated before destroying the editor
    subscriptions.add(editorSubscription)
  }

  return minimap
}

/**
 * Returns a new stand-alone {Minimap} for the passed-in `TextEditor`.
 *
 * @param  {TextEditor} textEditor a text editor instance to create
 *                                 a minimap for
 * @return {Minimap} a new stand-alone Minimap for the passed-in editor
 */
export function standAloneMinimapForEditor(textEditor) {
  if (!textEditor) {
    return
  }

  return new Minimap({
    textEditor,
    standAlone: true,
  })
}

/**
 * Returns the `Minimap` associated to the active `TextEditor`.
 *
 * @return {Minimap} the active Minimap
 */
export function getActiveMinimap() {
  return minimapForEditor(atom.workspace.getActiveTextEditor())
}

/**
 * Calls a function for each present and future minimaps.
 *
 * @param  {function(minimap:Minimap):void} iterator a function to call with
 *                                                   the existing and future
 *                                                   minimaps
 * @return {Disposable} a disposable to unregister the observer
 */
export function observeMinimaps(iterator) {
  if (!iterator) {
    return
  }

  if (editorsMinimaps) {
    editorsMinimaps.forEach((minimap) => {
      iterator(minimap)
    })
  }
  return onDidCreateMinimap((minimap) => {
    iterator(minimap)
  })
}

/**
 * Registers to the `observeTextEditors` method.
 *
 * @access private
 */
function initSubscriptions() {
  const debounceUpdateStyles = debounce(updateStyles, 300)
  subscriptions.add(
    atom.workspace.observeTextEditors((textEditor) => {
      const minimap = minimapForEditor(textEditor)
      const minimapElement = minimapViewProvider(minimap)

      emitter.emit("did-create-minimap", minimap)
      minimapElement.attach(textEditor.getElement())
    }),
    // empty color cache if the theme changes
    atom.themes.onDidChangeActiveThemes(debounceUpdateStyles),
    atom.styles.onDidUpdateStyleElement(debounceUpdateStyles),
    atom.styles.onDidAddStyleElement(debounceUpdateStyles),
    atom.styles.onDidRemoveStyleElement(debounceUpdateStyles),
    treeSitterWarning()
  )
}

function updateStyles() {
  console.log("update");
  domStylesReader.invalidateDOMStylesCache()
  editorsMinimaps.forEach((minimap) => {
    atom.views.getView(minimap).requestForcedUpdate()
  })
}

// The public exports included in the service:
const MinimapServiceV1 = {
  minimapViewProvider,
  getConfigSchema,
  onDidActivate,
  onDidDeactivate,
  onDidCreateMinimap,
  onDidAddPlugin,
  onDidRemovePlugin,
  onDidActivatePlugin,
  onDidDeactivatePlugin,
  onDidChangePluginOrder,
  minimapClass,
  minimapForEditorElement,
  minimapForEditor,
  standAloneMinimapForEditor,
  getActiveMinimap,
  observeMinimaps,
  registerPlugin: PluginManagement.registerPlugin,
  unregisterPlugin: PluginManagement.unregisterPlugin,
  togglePluginActivation: PluginManagement.togglePluginActivation,
  deactivateAllPlugins: PluginManagement.deactivateAllPlugins,
  activatePlugin: PluginManagement.activatePlugin,
  deactivatePlugin: PluginManagement.deactivatePlugin,
  getPluginsOrder: PluginManagement.getPluginsOrder,
}

/**
 * Returns the Minimap main module instance.
 *
 * @return {Main} The Minimap main module instance.
 */
export function provideMinimapServiceV1() {
  return MinimapServiceV1
}
<|endoftext|>


<commit_before>/* global Fae, SimpleMDE, toolbarBuiltInButtons */

'use strict';

/**
 * Fae form text
 * @namespace form.text
 * @memberof form
 */
Fae.form.text = {

  init: function() {
    this.slugger();
    this.overrideMarkdownDefaults();
    this.initMarkdown();
  },

  /**
   * Attach listeners to inputs and update slug fields with original safe values from the original inputs
   */
  slugger: function() {
    var _this = this;

    $('.slug').each(function() {
      var $form = $(this).closest('form');
      var $sluggers = $form.find('.slugger');
      var $slug = $form.find('.slug');
      var $select_slugger = $('.select.slugger');

      if ($slug.val() !== '') {
        $sluggers.removeClass('slugger');

      } else {
        // If it's a text field, listen for type input
        $sluggers.keyup(function(){
          var text = _this._digestSlug( $sluggers );
          $slug.val( text );
        });

        // If it's a select field, listen for the change
        if ($select_slugger.length) {
          $select_slugger.change(function(){
            var text = _this._digestSlug( $sluggers );
            $slug.val( text );
          });

        };
      }
    });
  },

  /**
   * Convert a group of selects or text fields into one slug string
   * @access protected
   * @param {jQuery} $sluggers - Input or inputs that should be converted into a URL-safe string
   * @return {String}
   */
  _digestSlug: function($sluggers) {
    var slug_text = []

    $sluggers.each(function() {
      var $this = $(this);

      if ($this.val().length) {

        if ($this.is('input')) {
          slug_text.push( $this.val() );

        } else {
          slug_text.push( $this.find('option:selected').text() );

        }
      }
    });

    // Combine all active slug fields for the query
    // Make them lowercase
    // Remove slashes, quotes or periods
    // Replace white spaces with a dash
    // Remove leading and trailing dashes
    slug_text = slug_text
      .join(' ')
      .toLowerCase()
      .replace(/(\\)|(\')|(\.)/g, '')
      .replace(/[^a-zA-Z0-9]+/g, '-')
      .replace(/(-$)|(^-)/g, '');

    return slug_text;
  },

  /**
   * Override SimpleMDE's preference for font-awesome icons and use a modal for the guide
   * @see {@link modals.markdownModal}
   */
  overrideMarkdownDefaults: function() {
    toolbarBuiltInButtons['bold'].className = 'icon-bold';
    toolbarBuiltInButtons['italic'].className = 'icon-italic';
    toolbarBuiltInButtons['heading'].className = 'icon-font';
    toolbarBuiltInButtons['code'].className = 'icon-code';
    toolbarBuiltInButtons['unordered-list'].className = 'icon-list-ul';
    toolbarBuiltInButtons['ordered-list'].className = 'icon-list-ol';
    toolbarBuiltInButtons['link'].className = 'icon-link';
    toolbarBuiltInButtons['image'].className = 'icon-image';
    toolbarBuiltInButtons['quote'].className = 'icon-quote';
    toolbarBuiltInButtons['fullscreen'].className = 'icon-fullscreen no-disable no-mobile';
    toolbarBuiltInButtons['horizontal-rule'].className = 'icon-minus';
    toolbarBuiltInButtons['preview'].className = 'icon-eye no-disable';
    toolbarBuiltInButtons['side-by-side'].className = 'icon-columns no-disable no-mobile';
    toolbarBuiltInButtons['guide'].className = 'icon-question';

    // Override SimpleMDE's default guide and use a homegrown modal
    toolbarBuiltInButtons['guide'].action = Fae.modals.markdownModal;
  },

  /**
   * Find all markdown fields and initialize them with a markdown GUI
   * @has_test {features/form_helpers/fae_input_spec.rb}
   */
  initMarkdown: function() {
    var fields = document.getElementsByClassName('js-markdown-editor');

    for(var i = 0; i < fields.length; i++) {
      var editor = new SimpleMDE({
        element: fields[i],
        autoDownloadFontAwesome: false,
        status: false,
        spellChecker: false,
        hideIcons: ['image', 'side-by-side', 'fullscreen']
      });
    }
  },

};
<commit_msg>refs #51357 - rm comma<commit_after>/* global Fae, SimpleMDE, toolbarBuiltInButtons */

'use strict';

/**
 * Fae form text
 * @namespace form.text
 * @memberof form
 */
Fae.form.text = {

  init: function() {
    this.slugger();
    this.overrideMarkdownDefaults();
    this.initMarkdown();
  },

  /**
   * Attach listeners to inputs and update slug fields with original safe values from the original inputs
   */
  slugger: function() {
    var _this = this;

    $('.slug').each(function() {
      var $form = $(this).closest('form');
      var $sluggers = $form.find('.slugger');
      var $slug = $form.find('.slug');
      var $select_slugger = $('.select.slugger');

      if ($slug.val() !== '') {
        $sluggers.removeClass('slugger');

      } else {
        // If it's a text field, listen for type input
        $sluggers.keyup(function(){
          var text = _this._digestSlug( $sluggers );
          $slug.val( text );
        });

        // If it's a select field, listen for the change
        if ($select_slugger.length) {
          $select_slugger.change(function(){
            var text = _this._digestSlug( $sluggers );
            $slug.val( text );
          });

        };
      }
    });
  },

  /**
   * Convert a group of selects or text fields into one slug string
   * @access protected
   * @param {jQuery} $sluggers - Input or inputs that should be converted into a URL-safe string
   * @return {String}
   */
  _digestSlug: function($sluggers) {
    var slug_text = []

    $sluggers.each(function() {
      var $this = $(this);

      if ($this.val().length) {

        if ($this.is('input')) {
          slug_text.push( $this.val() );

        } else {
          slug_text.push( $this.find('option:selected').text() );

        }
      }
    });

    // Combine all active slug fields for the query
    // Make them lowercase
    // Remove slashes, quotes or periods
    // Replace white spaces with a dash
    // Remove leading and trailing dashes
    slug_text = slug_text
      .join(' ')
      .toLowerCase()
      .replace(/(\\)|(\')|(\.)/g, '')
      .replace(/[^a-zA-Z0-9]+/g, '-')
      .replace(/(-$)|(^-)/g, '');

    return slug_text;
  },

  /**
   * Override SimpleMDE's preference for font-awesome icons and use a modal for the guide
   * @see {@link modals.markdownModal}
   */
  overrideMarkdownDefaults: function() {
    toolbarBuiltInButtons['bold'].className = 'icon-bold';
    toolbarBuiltInButtons['italic'].className = 'icon-italic';
    toolbarBuiltInButtons['heading'].className = 'icon-font';
    toolbarBuiltInButtons['code'].className = 'icon-code';
    toolbarBuiltInButtons['unordered-list'].className = 'icon-list-ul';
    toolbarBuiltInButtons['ordered-list'].className = 'icon-list-ol';
    toolbarBuiltInButtons['link'].className = 'icon-link';
    toolbarBuiltInButtons['image'].className = 'icon-image';
    toolbarBuiltInButtons['quote'].className = 'icon-quote';
    toolbarBuiltInButtons['fullscreen'].className = 'icon-fullscreen no-disable no-mobile';
    toolbarBuiltInButtons['horizontal-rule'].className = 'icon-minus';
    toolbarBuiltInButtons['preview'].className = 'icon-eye no-disable';
    toolbarBuiltInButtons['side-by-side'].className = 'icon-columns no-disable no-mobile';
    toolbarBuiltInButtons['guide'].className = 'icon-question';

    // Override SimpleMDE's default guide and use a homegrown modal
    toolbarBuiltInButtons['guide'].action = Fae.modals.markdownModal;
  },

  /**
   * Find all markdown fields and initialize them with a markdown GUI
   * @has_test {features/form_helpers/fae_input_spec.rb}
   */
  initMarkdown: function() {
    var fields = document.getElementsByClassName('js-markdown-editor');

    for(var i = 0; i < fields.length; i++) {
      var editor = new SimpleMDE({
        element: fields[i],
        autoDownloadFontAwesome: false,
        status: false,
        spellChecker: false,
        hideIcons: ['image', 'side-by-side', 'fullscreen']
      });
    }
  }

};
<|endoftext|>


<commit_before>'use babel'

let franc = null
let Promise = null
let CompositeDisposable = null
let Range = null
let UserSupportHelper = null
let Language = null
let TranslatorPlusDictionary = null
let TranslatorPlusDictionaryView = null
let NotificationView = null
let ExternalApis = null
let MicrosoftTranslatorClient = null
let Dejizo = null
let Configuration = null

let isInitialized = false
function initialize() {
  if (isInitialized) return

  // Initialize modules
  franc = require('franc')
  UserSupportHelper = require('atom-user-support-helper')
  Promise = require('bluebird')
  const atom = require('atom')
  CompositeDisposable = atom.CompositeDisposable
  Range = atom.Range

  // Initialize the local modules
  Language = require('./language')
  TranslatorPlusDictionary = require('./translator-plus-dictionary')
  TranslatorPlusDictionaryView = require('./translator-plus-dictionary-view')
  ExternalApis = require('./external-apis')
  NotificationView = require('./notification-view')
  MicrosoftTranslatorClient = require('./microsoft-translator-client')
  Dejizo = require('./dejizo')
  Configuration = require('./configuration')

  isInitialized = true
}

function getCodeFromName(name) {
  const lang = Language.getFromName(name)
  if (lang) return lang.code
  else return lang
}
function getNameFromCode(code) {
  const lang = Language.getFromCode(code)
  if (lang) return lang.name
  else return lang
}

export default {
  config: {
    microsoftTranslatorClientId: {
      title: 'Client ID of Microsoft Translator API',
      type: 'string',
      default: ''
    },
    microsoftTranslatorClientSecret: {
      title: 'Client Secret of Microsoft Translator API',
      type: 'string',
      default: ''
    },
    languages: {
      title: "Languages used in this package",
      description: "https://github.com/wooorm/franc/blob/master/Supported-Languages.md is a list of languages names.",
      type: 'array',
      default: ['eng', 'jpn'],
      items: {
        type: 'string'
      }
    },
    primaryLanguage: {
      title: "The Primary Language",
      type: 'string',
      default: 'jpn'
    },
    secondaryLanguage: {
      title: "The Secondary Language",
      type: 'string',
      default: 'eng'
    }
  },
  activate() {
    initialize()

    // Initialize the field
    this.subscriptions = new CompositeDisposable()
    this.languages = []
    this.francOptions = { whitelist: [], minLength: 1 }
    this.translatorPlusDictionary = new TranslatorPlusDictionary()
    this.notificationView = new NotificationView()
    this.views = new Map()

    // Initialize languages
    this.updateLanguages()

    // Add Microsoft Translator
    const microsoftTranslatorClientId = atom.config.get('translator-plus-dictionary.microsoftTranslatorClientId')
    const microsoftTranslatorClientSecret = atom.config.get('translator-plus-dictionary.microsoftTranslatorClientSecret')
    this.microsoftTranslatorClient = new MicrosoftTranslatorClient(microsoftTranslatorClientId, microsoftTranslatorClientSecret)
    this.translatorPlusDictionary.translators.push(this.microsoftTranslatorClient)

    // Add Dejizo
    const dejizo = new Dejizo()
    this.translatorPlusDictionary.dictionaries.push(dejizo)

    // Load external APIs
    const apis = new ExternalApis()
    apis.load(this.translatorPlusDictionary)

    // Wire translatorPlusDictionary and NotificationView
    this.subscriptions.add(
      this.translatorPlusDictionary.onStarted((x) => { this.notificationView.started(x) })
    )
    this.subscriptions.add(
      this.translatorPlusDictionary.onFinished((x) => { this.notificationView.finished(x) })
    )
    this.subscriptions.add(
      this.translatorPlusDictionary.onFailed((x) => { this.notificationView.failed(x) })
    )

    // Register the commands
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', { 'translator-plus-dictionary:translate': () => this.translate() })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', { 'translator-plus-dictionary:close-all': () => this.closeAll() })
    )
    this.subscriptions.add(
      atom.commands.add('body', { 'translator-plus-dictionary:configure': () => this.configure() })
    )

    // Observe the configuration
    this.subscriptions.add(
      atom.config.observe(Configuration.getKey(), (value) => {
        this.microsoftTranslatorClient.setClientIdAndSecret(
          value.microsoftTranslatorClientId,
          value.microsoftTranslatorClientSecret
        )
        this.updateLanguages()
      })
    )

    // Configure user-support-helper
    this.configureUserSupportHelper()
  },
  configureUserSupportHelper() {
    this.helper = new UserSupportHelper()

    // Add configuration settings
    const panel = this.helper.getInteractiveConfigurationPanel()
    panel.add(Configuration.getKey('microsoftTranslatorClientId'), {
      type: 'input',
      name: 'Client ID of Microsoft Translator API',
      detail: 'This page helps you to understand how to obtain the client ID.',
      validate: (result) => { return (result.length !== 0) ? true: 'too short' }
    })
    panel.add(Configuration.getKey('microsoftTranslatorClientSecret'), {
      type: 'input',
      name: 'Client Secret of Microsoft Translator API',
      validate: (result) => { return (result.length !== 0) ? true: 'too short' }
    })
    panel.add(Configuration.getKey('languages'), {
      type: 'multipleList',
      name: 'Languages used in this package',
      default: [],
      choices: Language.list.map((elem) => { return elem.name }),
      map: (names) => { return names.map((name) => { return getCodeFromName(name) }) },
      inverseMap: (codes) => { return codes.map((code) => { return getNameFromCode(code) }) },
      validate: (result) => {
        if (result.length >= 2) {
          return true
        } else {
          return "More than 2 language are required"
        }
      }
    })
    panel.add(Configuration.getKey('primaryLanguage'), {
      type: 'list',
      name: 'The primary language',
      message: "The recommended value is your native language.",
      default: '',
      choices: [],
      map: (name) => { return getCodeFromName(name) },
      inverseMap: (code) => { return getNameFromCode(code) }
    })
    panel.add(Configuration.getKey('secondaryLanguage'), {
      type: 'list',
      name: 'The secondary language',
      message: "The recommended value is 'eng'.",
      default: 'eng',
      choices: ['eng'],
      map: (name) => { return getCodeFromName(name) },
      inverseMap: (code) => { return getNameFromCode(code) }
    })
  },
  consumeStatusBar(statusBar) {
    // Add a tile to the status bar
    this.statusBarTile = statusBar.addLeftTile({
      item: this.notificationView.statusBar,
      priority: 100
    })
  },
  deactivate() {
    this.views.forEach((view) => view.destory())
    this.subscriptions.dispose()
    if (this.statusBarTile) {
      this.statusBarTile.destory()
    }
    this.notificationView.destory()
    this.helper.destroy()
  },
  serialize() {},
  closeView(editor, view) {
    for (let i = 0; i < this.views.get(editor).length; i++) {
      if (this.views.get(editor)[i] === view) {
        this.views.get(editor).slice(i, 1)
      }
    }
    view.destroy()

    if (this.views.get(editor).length === 0) {
      editor.getElement().classList.remove('translator-plus-dictionary-active')
    }
  },
  closeAll() {
    const editor = atom.workspace.getActiveTextEditor()

    if (!editor || !this.views.has(editor)) return

    for (const v of this.views.get(editor)) {
      v.destroy()
    }
    this.views.delete(editor)
    editor.getElement().classList.remove('translator-plus-dictionary-active')
  },
  configure() {
    const panel = this.helper.getInteractiveConfigurationPanel()
    return panel.show((id, configValues) => {
      switch (id) {
        case null:
          return { id: Configuration.getKey("microsoftTranslatorClientId") }
        case Configuration.getKey("microsoftTranslatorClientId"):
          return { id: Configuration.getKey("microsoftTranslatorClientSecret") }
        case Configuration.getKey("microsoftTranslatorClientSecret"):
          return { id: Configuration.getKey("languages") }
        case Configuration.getKey("languages"):
          return {
            id: Configuration.getKey("primaryLanguage"),
            choices: configValues.get(Configuration.getKey("languages")).map((code) => {
              return getNameFromCode(code)
            }),
          }
        case Configuration.getKey("primaryLanguage"):
          return {
            id: Configuration.getKey("secondaryLanguage"),
            choices: configValues.get(Configuration.getKey("languages")).map((code) => {
              return getNameFromCode(code)
            }),
            finished: true
          }
        default:
          return null
      }
    }).then((result) => {
      atom.config.set(Configuration.getKey("configured"), true)
      return result
    })
  },
  configureIfNeeded() {
    const isSetClientIdAndSecret =
      (
        Configuration.getValueWithoutDefault("microsoftTranslatorClientId") &&
        Configuration.getValueWithoutDefault("microsoftTranslatorClientSecret")
      )
    const isSetLanguage =
      (
        Configuration.getValueWithoutDefault("languages") ||
        Configuration.getValueWithoutDefault("secondaryLanguage") ||
        Configuration.getValueWithoutDefault("secondaryLanguage")
      )
    const alreadyConfigured = Configuration.getValue("configured") || false
    if (isSetClientIdAndSecret && (isSetLanguage || alreadyConfigured)) {
      return new Promise((resolve) => resolve())
    } else {
      return this.configure()
    }
  },
  translate() {
    this.configureIfNeeded().then(
      () => {
        // Execute after configuration
        const editor = atom.workspace.getActiveTextEditor()

        if (!editor) return

        atom.workspace.getActiveTextEditor().getElement().classList.add('translator-plus-dictionary-active')

        let targets = []

        // Get texts from selected texts
        for (const range of editor.getSelectedBufferRanges()) {
          if ((range.start.row === range.end.row) && (range.start.column === range.end.column)) continue
          text = editor.getTextInRange(range)
          targets.push({
            range: range,
            text: text
          })
        }

        if (targets.length === 0) {
          // Get words from cursor positions if no text are selected
          for (const cursor of editor.getCursors()) {
            const beginWord = cursor.getBeginningOfCurrentWordBufferPosition()
            const endWord = cursor.getEndOfCurrentWordBufferPosition()
            const range = new Range(beginWord, endWord)

            targets.push({
              range: range,
              text: editor.getTextInRange(range)
            })
          }
        }

        for (const target of targets) {
          // Decide the language of the texts by using frac
          const from = franc(target.text, this.francOptions)
          const lang = Language.getFromCode(from)
          target.from = lang

          // Decide the output language by using the primary and secondary languages
          if (this.primaryLanguage === lang) {
            target.to = this.secondaryLanguage
          } else {
            target.to = this.primaryLanguage
          }

          // Generate a view
          const view = new TranslatorPlusDictionaryView(
            editor, target, this.languages, this.translatorPlusDictionary
          )

          // Add to views
          if (!this.views.has(editor)) {
            this.views.set(editor, [])
          }
          this.views.get(editor).push(view)
          view.onClosed(() => { this.closeView(editor, view) })
        }
      }
    )
  },
  updateLanguages() {
    const languageCodes = atom.config.get('translator-plus-dictionary.languages')
    languageCodes.forEach((code) => {
      language = Language.getFromCode(code)
      if (language === null) {
        // TODO notify anerror
        return
      }

      // Store the usable languages information in the form of Language class instances
      this.languages.push(language)
      this.francOptions.whitelist.push(code)
    })

    if (this.languages.length === 0) {
      // Use all languages detected by franc if there is no `languages` configuration.
      this.languages = Language.list
      this.francOptions.whiteList = Language.list.map((e) => { return e.code })
    }

    this.primaryLanguage = Language.getFromCode(atom.config.get('translator-plus-dictionary.primaryLanguage'))
    this.secondaryLanguage = Language.getFromCode(atom.config.get('translator-plus-dictionary.secondaryLanguage'))
  }
}
<commit_msg>Fix the bug in updateLanguages<commit_after>'use babel'

let franc = null
let Promise = null
let CompositeDisposable = null
let Range = null
let UserSupportHelper = null
let Language = null
let TranslatorPlusDictionary = null
let TranslatorPlusDictionaryView = null
let NotificationView = null
let ExternalApis = null
let MicrosoftTranslatorClient = null
let Dejizo = null
let Configuration = null

let isInitialized = false
function initialize() {
  if (isInitialized) return

  // Initialize modules
  franc = require('franc')
  UserSupportHelper = require('atom-user-support-helper')
  Promise = require('bluebird')
  const atom = require('atom')
  CompositeDisposable = atom.CompositeDisposable
  Range = atom.Range

  // Initialize the local modules
  Language = require('./language')
  TranslatorPlusDictionary = require('./translator-plus-dictionary')
  TranslatorPlusDictionaryView = require('./translator-plus-dictionary-view')
  ExternalApis = require('./external-apis')
  NotificationView = require('./notification-view')
  MicrosoftTranslatorClient = require('./microsoft-translator-client')
  Dejizo = require('./dejizo')
  Configuration = require('./configuration')

  isInitialized = true
}

function getCodeFromName(name) {
  const lang = Language.getFromName(name)
  if (lang) return lang.code
  else return lang
}
function getNameFromCode(code) {
  const lang = Language.getFromCode(code)
  if (lang) return lang.name
  else return lang
}

export default {
  config: {
    microsoftTranslatorClientId: {
      title: 'Client ID of Microsoft Translator API',
      type: 'string',
      default: ''
    },
    microsoftTranslatorClientSecret: {
      title: 'Client Secret of Microsoft Translator API',
      type: 'string',
      default: ''
    },
    languages: {
      title: "Languages used in this package",
      description: "https://github.com/wooorm/franc/blob/master/Supported-Languages.md is a list of languages names.",
      type: 'array',
      default: ['eng', 'jpn'],
      items: {
        type: 'string'
      }
    },
    primaryLanguage: {
      title: "The Primary Language",
      type: 'string',
      default: 'jpn'
    },
    secondaryLanguage: {
      title: "The Secondary Language",
      type: 'string',
      default: 'eng'
    }
  },
  activate() {
    initialize()

    // Initialize the field
    this.subscriptions = new CompositeDisposable()
    this.languages = []
    this.francOptions = { whitelist: [], minLength: 1 }
    this.translatorPlusDictionary = new TranslatorPlusDictionary()
    this.notificationView = new NotificationView()
    this.views = new Map()

    // Initialize languages
    this.updateLanguages()

    // Add Microsoft Translator
    const microsoftTranslatorClientId = atom.config.get('translator-plus-dictionary.microsoftTranslatorClientId')
    const microsoftTranslatorClientSecret = atom.config.get('translator-plus-dictionary.microsoftTranslatorClientSecret')
    this.microsoftTranslatorClient = new MicrosoftTranslatorClient(microsoftTranslatorClientId, microsoftTranslatorClientSecret)
    this.translatorPlusDictionary.translators.push(this.microsoftTranslatorClient)

    // Add Dejizo
    const dejizo = new Dejizo()
    this.translatorPlusDictionary.dictionaries.push(dejizo)

    // Load external APIs
    const apis = new ExternalApis()
    apis.load(this.translatorPlusDictionary)

    // Wire translatorPlusDictionary and NotificationView
    this.subscriptions.add(
      this.translatorPlusDictionary.onStarted((x) => { this.notificationView.started(x) })
    )
    this.subscriptions.add(
      this.translatorPlusDictionary.onFinished((x) => { this.notificationView.finished(x) })
    )
    this.subscriptions.add(
      this.translatorPlusDictionary.onFailed((x) => { this.notificationView.failed(x) })
    )

    // Register the commands
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', { 'translator-plus-dictionary:translate': () => this.translate() })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', { 'translator-plus-dictionary:close-all': () => this.closeAll() })
    )
    this.subscriptions.add(
      atom.commands.add('body', { 'translator-plus-dictionary:configure': () => this.configure() })
    )

    // Observe the configuration
    this.subscriptions.add(
      atom.config.observe(Configuration.getKey(), (value) => {
        this.microsoftTranslatorClient.setClientIdAndSecret(
          value.microsoftTranslatorClientId,
          value.microsoftTranslatorClientSecret
        )
        this.updateLanguages()
      })
    )

    // Configure user-support-helper
    this.configureUserSupportHelper()
  },
  configureUserSupportHelper() {
    this.helper = new UserSupportHelper()

    // Add configuration settings
    const panel = this.helper.getInteractiveConfigurationPanel()
    panel.add(Configuration.getKey('microsoftTranslatorClientId'), {
      type: 'input',
      name: 'Client ID of Microsoft Translator API',
      detail: 'This page helps you to understand how to obtain the client ID.',
      validate: (result) => { return (result.length !== 0) ? true: 'too short' }
    })
    panel.add(Configuration.getKey('microsoftTranslatorClientSecret'), {
      type: 'input',
      name: 'Client Secret of Microsoft Translator API',
      validate: (result) => { return (result.length !== 0) ? true: 'too short' }
    })
    panel.add(Configuration.getKey('languages'), {
      type: 'multipleList',
      name: 'Languages used in this package',
      default: [],
      choices: Language.list.map((elem) => { return elem.name }),
      map: (names) => { return names.map((name) => { return getCodeFromName(name) }) },
      inverseMap: (codes) => { return codes.map((code) => { return getNameFromCode(code) }) },
      validate: (result) => {
        if (result.length >= 2) {
          return true
        } else {
          return "More than 2 language are required"
        }
      }
    })
    panel.add(Configuration.getKey('primaryLanguage'), {
      type: 'list',
      name: 'The primary language',
      message: "The recommended value is your native language.",
      default: '',
      choices: [],
      map: (name) => { return getCodeFromName(name) },
      inverseMap: (code) => { return getNameFromCode(code) }
    })
    panel.add(Configuration.getKey('secondaryLanguage'), {
      type: 'list',
      name: 'The secondary language',
      message: "The recommended value is 'eng'.",
      default: 'eng',
      choices: ['eng'],
      map: (name) => { return getCodeFromName(name) },
      inverseMap: (code) => { return getNameFromCode(code) }
    })
  },
  consumeStatusBar(statusBar) {
    // Add a tile to the status bar
    this.statusBarTile = statusBar.addLeftTile({
      item: this.notificationView.statusBar,
      priority: 100
    })
  },
  deactivate() {
    this.views.forEach((view) => view.destory())
    this.subscriptions.dispose()
    if (this.statusBarTile) {
      this.statusBarTile.destory()
    }
    this.notificationView.destory()
    this.helper.destroy()
  },
  serialize() {},
  closeView(editor, view) {
    for (let i = 0; i < this.views.get(editor).length; i++) {
      if (this.views.get(editor)[i] === view) {
        this.views.get(editor).slice(i, 1)
      }
    }
    view.destroy()

    if (this.views.get(editor).length === 0) {
      editor.getElement().classList.remove('translator-plus-dictionary-active')
    }
  },
  closeAll() {
    const editor = atom.workspace.getActiveTextEditor()

    if (!editor || !this.views.has(editor)) return

    for (const v of this.views.get(editor)) {
      v.destroy()
    }
    this.views.delete(editor)
    editor.getElement().classList.remove('translator-plus-dictionary-active')
  },
  configure() {
    const panel = this.helper.getInteractiveConfigurationPanel()
    return panel.show((id, configValues) => {
      switch (id) {
        case null:
          return { id: Configuration.getKey("microsoftTranslatorClientId") }
        case Configuration.getKey("microsoftTranslatorClientId"):
          return { id: Configuration.getKey("microsoftTranslatorClientSecret") }
        case Configuration.getKey("microsoftTranslatorClientSecret"):
          return { id: Configuration.getKey("languages") }
        case Configuration.getKey("languages"):
          return {
            id: Configuration.getKey("primaryLanguage"),
            choices: configValues.get(Configuration.getKey("languages")).map((code) => {
              return getNameFromCode(code)
            }),
          }
        case Configuration.getKey("primaryLanguage"):
          return {
            id: Configuration.getKey("secondaryLanguage"),
            choices: configValues.get(Configuration.getKey("languages")).map((code) => {
              return getNameFromCode(code)
            }),
            finished: true
          }
        default:
          return null
      }
    }).then((result) => {
      atom.config.set(Configuration.getKey("configured"), true)
      return result
    })
  },
  configureIfNeeded() {
    const isSetClientIdAndSecret =
      (
        Configuration.getValueWithoutDefault("microsoftTranslatorClientId") &&
        Configuration.getValueWithoutDefault("microsoftTranslatorClientSecret")
      )
    const isSetLanguage =
      (
        Configuration.getValueWithoutDefault("languages") ||
        Configuration.getValueWithoutDefault("secondaryLanguage") ||
        Configuration.getValueWithoutDefault("secondaryLanguage")
      )
    const alreadyConfigured = Configuration.getValue("configured") || false
    if (isSetClientIdAndSecret && (isSetLanguage || alreadyConfigured)) {
      return new Promise((resolve) => resolve())
    } else {
      return this.configure()
    }
  },
  translate() {
    this.configureIfNeeded().then(
      () => {
        // Execute after configuration
        const editor = atom.workspace.getActiveTextEditor()

        if (!editor) return

        atom.workspace.getActiveTextEditor().getElement().classList.add('translator-plus-dictionary-active')

        let targets = []

        // Get texts from selected texts
        for (const range of editor.getSelectedBufferRanges()) {
          if ((range.start.row === range.end.row) && (range.start.column === range.end.column)) continue
          text = editor.getTextInRange(range)
          targets.push({
            range: range,
            text: text
          })
        }

        if (targets.length === 0) {
          // Get words from cursor positions if no text are selected
          for (const cursor of editor.getCursors()) {
            const beginWord = cursor.getBeginningOfCurrentWordBufferPosition()
            const endWord = cursor.getEndOfCurrentWordBufferPosition()
            const range = new Range(beginWord, endWord)

            targets.push({
              range: range,
              text: editor.getTextInRange(range)
            })
          }
        }

        for (const target of targets) {
          // Decide the language of the texts by using frac
          const from = franc(target.text, this.francOptions)
          const lang = Language.getFromCode(from)
          target.from = lang

          // Decide the output language by using the primary and secondary languages
          if (this.primaryLanguage === lang) {
            target.to = this.secondaryLanguage
          } else {
            target.to = this.primaryLanguage
          }

          // Generate a view
          const view = new TranslatorPlusDictionaryView(
            editor, target, this.languages, this.translatorPlusDictionary
          )

          // Add to views
          if (!this.views.has(editor)) {
            this.views.set(editor, [])
          }
          this.views.get(editor).push(view)
          view.onClosed(() => { this.closeView(editor, view) })
        }
      }
    )
  },
  updateLanguages() {
    const languageCodes = atom.config.get('translator-plus-dictionary.languages')
    this.languages = []
    languageCodes.forEach((code) => {
      language = Language.getFromCode(code)
      if (language === null) {
        // TODO notify anerror
        return
      }

      // Store the usable languages information in the form of Language class instances
      this.languages.push(language)
      this.francOptions.whitelist.push(code)
    })

    if (this.languages.length === 0) {
      // Use all languages detected by franc if there is no `languages` configuration.
      this.languages = Language.list
      this.francOptions.whiteList = Language.list.map((e) => { return e.code })
    }

    this.primaryLanguage = Language.getFromCode(atom.config.get('translator-plus-dictionary.primaryLanguage'))
    this.secondaryLanguage = Language.getFromCode(atom.config.get('translator-plus-dictionary.secondaryLanguage'))
  }
}
<|endoftext|>


<commit_before>/*jslint browser: true, indent: 2, todo: true, unparam: true */
/*global jQuery,Ornament /*/

(function (document, window, $) {

  "use strict";

  $(document).on("ornament:toggles", function () {

    var $toggleAnchors = $("[data-toggle-anchor]");
    var $toggles = $("[data-toggle]");
    var $temporaryToggles = $("[data-toggle-temporary]");
    var toggleOnClass = "active";
    var headerBreakpoint = Ornament.headerBreakpoint;

    var toggleOn = function($toggleAnchor, $toggleContent) {
      var toggleId = $toggleAnchor.attr("data-toggle-anchor");
      var $thisToggleAnchors = $("[data-toggle-anchor="+toggleId+"]");
      var focusOnField = $toggleAnchor.is("[data-toggle-focus]");

      // Toggle a visible class
      $thisToggleAnchors.addClass(toggleOnClass);

      // Swap label if needed
      if($toggleAnchor.is("[data-toggle-visible-label]")) {
        $toggleAnchor.text($toggleAnchor.attr("data-toggle-visible-label"));
      }

      // Hide other toggle anchors if needed
      if($toggleAnchor.is("[data-toggle-group]")) {
        var toggleGroupId = $toggleAnchor.attr("data-toggle-group");
        var $otherToggleAnchors = $toggleAnchors.filter("[data-toggle-group="+toggleGroupId+"]").not($toggleAnchor);
        $otherToggleAnchors.each(function(){
          var $otherToggleAnchor = $(this);
          var otherToggleId = $otherToggleAnchor.attr("data-toggle-anchor");
          var $otherToggleContent = $("[data-toggle="+otherToggleId+"]");
          toggleOff($otherToggleAnchor, $otherToggleContent);
        });
      }

      // Setup afterSlide function
      var afterSlide = function($toggleContent, $toggleAnchor, on){
        var on = on || false;
        if(on && focusOnField) {
          var $inputs = $toggleContent.find("input");
          var $textareas = $toggleContent.find("textarea");
          if($inputs.length) {
            $inputs.first().focus();
          } else if($textareas.length) {
            $textareas.first().focus();
          }
        }
        Ornament.globalLightboxSizing();
      }

      // Toggle sliding
      if($toggleAnchor.is("[data-toggle-tab]")) {
        $toggleContent.addClass("tabs--pane__active");
      } else {
        var speed = $toggleAnchor.is("[data-toggle-speed]") ? parseInt($toggleAnchor.attr("data-toggle-speed")) : 200;
        $toggleContent.stop().slideDown(speed, function(){
          afterSlide($toggleContent, $toggleAnchor, true);
        });
      }

      $toggleAnchor.trigger("ornament:toggle:after-toggle-on");
    }

    var toggleOff = function($toggleAnchor, $toggleContent) {
      var toggleId = $toggleAnchor.attr("data-toggle-anchor");
      var $thisToggleAnchors = $("[data-toggle-anchor="+toggleId+"]");

      // Toggle visible class
      $thisToggleAnchors.removeClass(toggleOnClass);

      // Swap label if needed
      if($toggleAnchor.is("[data-toggle-hidden-label]")) {
        $toggleAnchor.text($toggleAnchor.attr("data-toggle-hidden-label"));
      }

      // Slide content
      if($toggleContent.is("[data-toggle-tab-pane]")) {
        $toggleContent.removeClass("tabs--pane__active");
      } else {
        var speed = $toggleAnchor.is("[data-toggle-speed]") ? parseInt($toggleAnchor.attr("data-toggle-speed")) : 200;
        $toggleContent.stop().slideUp(speed, function(){
          afterSlide($toggleContent, $toggleAnchor);
        });
      }

      // Scroll if needed
      if($toggleAnchor.is("[data-toggle-scroll]")) {
        // Find the master toggle and find it's offset top
        var $toggleMaster = $thisToggleAnchors.filter("[data-toggle-scroll-master]");
        var masterOffsetTop = $toggleMaster.offset().top;

        // Account for sticky header
        // masterOffsetTop = masterOffsetTop - Ornament.stickyHeightComparison(masterOffsetTop);

        // Check if user has scrolled past the master element
        // we don't want the user to be scrolled down, only up
        var scrollTop = $(document).scrollTop();
        if(scrollTop > masterOffsetTop) {
          // Animate up
          $("html,body").animate({
            scrollTop: masterOffsetTop
          }, 200);
        }
      }
    }

    var toggle = function($toggleAnchor, $toggleContent) {

      var oneWay = false;
      if($toggleAnchor.is("[data-toggle-one-way]")) {
        oneWay = true;
      }

      // Abort if animating
      if($toggleContent.is(":animated")) {
        return false;
      }

      // Abort if one-way and already active
      if(oneWay && $toggleAnchor.is("."+toggleOnClass)) {
        return false;
      }

      if($toggleAnchor.hasClass(toggleOnClass) && !oneWay) {
        toggleOff($toggleAnchor, $toggleContent);
      } else {
        toggleOn($toggleAnchor, $toggleContent);
      }

      $toggleAnchor.trigger("toggle:toggled");
    }

    // Hid all toggles on page
    var hideAllToggles = function(){
      $toggles.not("[data-toggle-default]").each(function(){
        var $togglePane = $(this);
        if($("[data-toggle-anchor=" + $togglePane.attr("data-toggle") + "]").is(".active")) {
          //
        } else if($togglePane.is("[data-toggle-tab-pane]")) {
          //
        } else {
          $togglePane.hide();
        }
      });
    }

    Ornament.toggle = function($toggleAnchor, $toggleContent) {
      toggle($toggleAnchor, $toggleContent);
    }

    // Hide all toggles by default
    hideAllToggles();

    $toggleAnchors.not("[data-toggle-anchor-init]").each(function(){
      var $toggleAnchor = $(this);
      var toggleId = $toggleAnchor.attr("data-toggle-anchor");
      var $toggleContent = $("[data-toggle=" + toggleId + "]");

      // Cache old text value if we need to swap labels
      if($toggleAnchor.is("[data-toggle-visible-label]")) {
        $toggleAnchor.attr("data-toggle-hidden-label", $toggleAnchor.text());
      }

      // Clicking on anchors
      $toggleAnchor.on("click", function(e){
        e.preventDefault();
        toggle($toggleAnchor, $toggleContent);
      });

      // Bind some events
      $toggleAnchor.bind("ornament:toggle-on", function(){
        toggleOn($toggleAnchor, $toggleContent);
      });
      $toggleAnchor.bind("ornament:toggle-off", function(){
        toggleOff($toggleAnchor, $toggleContent);
      });
      $toggleAnchor.bind("ornament:toggle", function(){
        toggle($toggleAnchor, $toggleContent);
      });
      $(document).bind("ornament:toggle-all-off", function(){
        hideAllToggles();
      });

    }).attr("data-toggle-anchor-init", "");

    var hideTemporaryToggles = function(){
      $temporaryToggles.each(function(){
        var $toggleContent = $(this);
        var toggleId = $toggleContent.attr("data-toggle");
        var $toggleAnchor = $("[data-toggle-anchor=" + toggleId + "]");
        toggleOff($toggleAnchor, $toggleContent);
      });
    }

    // If the user clicks on something (or tab to something) outside of the nav,
    // collapse any relevant toggles
    $("html").on("click onfocus touch", function () {
      hideTemporaryToggles();
    });

    // Prevent "click" and "onfocus" events from propagating throught to the
    // HTML element.
    $temporaryToggles.on("click onfocus touch", function (event) {
      event.stopPropagation();
    });
    $("[data-toggle-temporary-anchor]").on("click onfocus touch", function (event) {
      event.stopPropagation();
    });

  });

  $(document).on("ornament:refresh", function () {
    $(document).trigger("ornament:toggles");
  });

}(document, window, jQuery));<commit_msg>Fixed undefined function afterSlide error when closing flash messages<commit_after>/*jslint browser: true, indent: 2, todo: true, unparam: true */
/*global jQuery,Ornament /*/

(function (document, window, $) {

  "use strict";

  $(document).on("ornament:toggles", function () {

    var $toggleAnchors = $("[data-toggle-anchor]");
    var $toggles = $("[data-toggle]");
    var $temporaryToggles = $("[data-toggle-temporary]");
    var toggleOnClass = "active";
    var headerBreakpoint = Ornament.headerBreakpoint;

    // Setup afterSlide function
    var afterSlide = function($toggleContent, $toggleAnchor, on){
      var on = on || false;
      if(on && focusOnField) {
        var $inputs = $toggleContent.find("input");
        var $textareas = $toggleContent.find("textarea");
        if($inputs.length) {
          $inputs.first().focus();
        } else if($textareas.length) {
          $textareas.first().focus();
        }
      }
      Ornament.globalLightboxSizing();
    }

    var toggleOn = function($toggleAnchor, $toggleContent) {
      var toggleId = $toggleAnchor.attr("data-toggle-anchor");
      var $thisToggleAnchors = $("[data-toggle-anchor="+toggleId+"]");
      var focusOnField = $toggleAnchor.is("[data-toggle-focus]");

      // Toggle a visible class
      $thisToggleAnchors.addClass(toggleOnClass);

      // Swap label if needed
      if($toggleAnchor.is("[data-toggle-visible-label]")) {
        $toggleAnchor.text($toggleAnchor.attr("data-toggle-visible-label"));
      }

      // Hide other toggle anchors if needed
      if($toggleAnchor.is("[data-toggle-group]")) {
        var toggleGroupId = $toggleAnchor.attr("data-toggle-group");
        var $otherToggleAnchors = $toggleAnchors.filter("[data-toggle-group="+toggleGroupId+"]").not($toggleAnchor);
        $otherToggleAnchors.each(function(){
          var $otherToggleAnchor = $(this);
          var otherToggleId = $otherToggleAnchor.attr("data-toggle-anchor");
          var $otherToggleContent = $("[data-toggle="+otherToggleId+"]");
          toggleOff($otherToggleAnchor, $otherToggleContent);
        });
      }

      // Toggle sliding
      if($toggleAnchor.is("[data-toggle-tab]")) {
        $toggleContent.addClass("tabs--pane__active");
      } else {
        var speed = $toggleAnchor.is("[data-toggle-speed]") ? parseInt($toggleAnchor.attr("data-toggle-speed")) : 200;
        $toggleContent.stop().slideDown(speed, function(){
          afterSlide($toggleContent, $toggleAnchor, true);
        });
      }

      $toggleAnchor.trigger("ornament:toggle:after-toggle-on");
    }

    var toggleOff = function($toggleAnchor, $toggleContent) {
      var toggleId = $toggleAnchor.attr("data-toggle-anchor");
      var $thisToggleAnchors = $("[data-toggle-anchor="+toggleId+"]");

      // Toggle visible class
      $thisToggleAnchors.removeClass(toggleOnClass);

      // Swap label if needed
      if($toggleAnchor.is("[data-toggle-hidden-label]")) {
        $toggleAnchor.text($toggleAnchor.attr("data-toggle-hidden-label"));
      }

      // Slide content
      if($toggleContent.is("[data-toggle-tab-pane]")) {
        $toggleContent.removeClass("tabs--pane__active");
      } else {
        var speed = $toggleAnchor.is("[data-toggle-speed]") ? parseInt($toggleAnchor.attr("data-toggle-speed")) : 200;
        $toggleContent.stop().slideUp(speed, function(){
          afterSlide($toggleContent, $toggleAnchor);
        });
      }

      // Scroll if needed
      if($toggleAnchor.is("[data-toggle-scroll]")) {
        // Find the master toggle and find it's offset top
        var $toggleMaster = $thisToggleAnchors.filter("[data-toggle-scroll-master]");
        var masterOffsetTop = $toggleMaster.offset().top;

        // Account for sticky header
        // masterOffsetTop = masterOffsetTop - Ornament.stickyHeightComparison(masterOffsetTop);

        // Check if user has scrolled past the master element
        // we don't want the user to be scrolled down, only up
        var scrollTop = $(document).scrollTop();
        if(scrollTop > masterOffsetTop) {
          // Animate up
          $("html,body").animate({
            scrollTop: masterOffsetTop
          }, 200);
        }
      }
    }

    var toggle = function($toggleAnchor, $toggleContent) {

      var oneWay = false;
      if($toggleAnchor.is("[data-toggle-one-way]")) {
        oneWay = true;
      }

      // Abort if animating
      if($toggleContent.is(":animated")) {
        return false;
      }

      // Abort if one-way and already active
      if(oneWay && $toggleAnchor.is("."+toggleOnClass)) {
        return false;
      }

      if($toggleAnchor.hasClass(toggleOnClass) && !oneWay) {
        toggleOff($toggleAnchor, $toggleContent);
      } else {
        toggleOn($toggleAnchor, $toggleContent);
      }

      $toggleAnchor.trigger("toggle:toggled");
    }

    // Hid all toggles on page
    var hideAllToggles = function(){
      $toggles.not("[data-toggle-default]").each(function(){
        var $togglePane = $(this);
        if($("[data-toggle-anchor=" + $togglePane.attr("data-toggle") + "]").is(".active")) {
          //
        } else if($togglePane.is("[data-toggle-tab-pane]")) {
          //
        } else {
          $togglePane.hide();
        }
      });
    }

    Ornament.toggle = function($toggleAnchor, $toggleContent) {
      toggle($toggleAnchor, $toggleContent);
    }

    // Hide all toggles by default
    hideAllToggles();

    $toggleAnchors.not("[data-toggle-anchor-init]").each(function(){
      var $toggleAnchor = $(this);
      var toggleId = $toggleAnchor.attr("data-toggle-anchor");
      var $toggleContent = $("[data-toggle=" + toggleId + "]");

      // Cache old text value if we need to swap labels
      if($toggleAnchor.is("[data-toggle-visible-label]")) {
        $toggleAnchor.attr("data-toggle-hidden-label", $toggleAnchor.text());
      }

      // Clicking on anchors
      $toggleAnchor.on("click", function(e){
        e.preventDefault();
        toggle($toggleAnchor, $toggleContent);
      });

      // Bind some events
      $toggleAnchor.bind("ornament:toggle-on", function(){
        toggleOn($toggleAnchor, $toggleContent);
      });
      $toggleAnchor.bind("ornament:toggle-off", function(){
        toggleOff($toggleAnchor, $toggleContent);
      });
      $toggleAnchor.bind("ornament:toggle", function(){
        toggle($toggleAnchor, $toggleContent);
      });
      $(document).bind("ornament:toggle-all-off", function(){
        hideAllToggles();
      });

    }).attr("data-toggle-anchor-init", "");

    var hideTemporaryToggles = function(){
      $temporaryToggles.each(function(){
        var $toggleContent = $(this);
        var toggleId = $toggleContent.attr("data-toggle");
        var $toggleAnchor = $("[data-toggle-anchor=" + toggleId + "]");
        toggleOff($toggleAnchor, $toggleContent);
      });
    }

    // If the user clicks on something (or tab to something) outside of the nav,
    // collapse any relevant toggles
    $("html").on("click onfocus touch", function () {
      hideTemporaryToggles();
    });

    // Prevent "click" and "onfocus" events from propagating throught to the
    // HTML element.
    $temporaryToggles.on("click onfocus touch", function (event) {
      event.stopPropagation();
    });
    $("[data-toggle-temporary-anchor]").on("click onfocus touch", function (event) {
      event.stopPropagation();
    });

  });

  $(document).on("ornament:refresh", function () {
    $(document).trigger("ornament:toggles");
  });

}(document, window, jQuery));<|endoftext|>