<commit_before>// @flow
import os from 'os'
import { app, Menu, shell, BrowserWindow, ipcMain } from 'electron'
import openAboutWindow from './about'
import { getLanguageName, locales } from '../i18n'
import { getPackageDetails } from '../utils'

import { appRootPath } from '../lnd/util'

const path = require('path')

const buildAboutMenu = () => {
  return {
    label: 'About Zap',
    click: () => {
      const { productName, version } = getPackageDetails()
      openAboutWindow(
        {
          icon_path: path.resolve(appRootPath(), 'resources', 'icons', 'icon.png'),
          open_devtools: process.env.NODE_ENV !== 'production',
          product_name: `${productName} ${version}`
        },
        `file://${path.resolve(
          process.env.HOT ? 'app' : app.getAppPath(),
          'lib/zap/about/public/',
          'about.html'
        )}`
      )
    }
  }
}

export default class ZapMenuBuilder {
  mainWindow: BrowserWindow
  locale: string

  constructor(mainWindow: BrowserWindow) {
    this.mainWindow = mainWindow
    ipcMain.on('setLocale', (event, locale) => this.buildMenu(locale))
  }

  buildMenu(locale?: string) {
    if (locale) {
      this.locale = locale
    }

    let template
    if (os.platform() === 'darwin') {
      template = this.buildDarwinTemplate()
    } else {
      template = this.buildDefaultTemplate()
    }

    this.setupInputTemplate()

    const menu = Menu.buildFromTemplate(template)
    Menu.setApplicationMenu(menu)

    return menu
  }

  setupInputTemplate() {
    const selectionMenu = Menu.buildFromTemplate([
      { role: 'copy' },
      { type: 'separator' },
      { role: 'selectall' }
    ])

    const inputMenu = Menu.buildFromTemplate([
      { role: 'undo' },
      { role: 'redo' },
      { type: 'separator' },
      { role: 'cut' },
      { role: 'copy' },
      { role: 'paste' },
      { type: 'separator' },
      { role: 'selectall' }
    ])

    this.mainWindow.webContents.on('context-menu', (e, props) => {
      const { selectionText, isEditable } = props

      if (isEditable) {
        inputMenu.popup(this.mainWindow)
      } else if (selectionText && selectionText.trim() !== '') {
        selectionMenu.popup(this.mainWindow)
      }
    })
  }

  buildDarwinTemplate() {
    const subMenuAbout = {
      label: 'Electron',
      submenu: [
        buildAboutMenu(),
        { type: 'separator' },
        { label: 'Hide Zap', accelerator: 'Command+H', selector: 'hide:' },
        {
          label: 'Hide Others',
          accelerator: 'Command+Shift+H',
          selector: 'hideOtherApplications:'
        },
        { label: 'Show All', selector: 'unhideAllApplications:' },
        { type: 'separator' },
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: () => {
            app.quit()
          }
        }
      ]
    }
    const subMenuEdit = {
      label: 'Edit',
      submenu: [
        { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
        { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
        { type: 'separator' },
        { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
        { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
        { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
        { label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' }
      ]
    }
    const subMenuViewDev = {
      label: 'View',
      submenu: [
        {
          label: 'Reload',
          accelerator: 'Command+R',
          click: () => {
            this.mainWindow.webContents.reload()
          }
        },
        {
          label: 'Toggle Full Screen',
          accelerator: 'Ctrl+Command+F',
          click: () => {
            this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
          }
        },
        {
          label: 'Toggle Developer Tools',
          accelerator: 'Alt+Command+I',
          click: () => {
            this.mainWindow.toggleDevTools()
          }
        }
      ]
    }
    const subMenuViewProd = {
      label: 'View',
      submenu: [
        {
          label: 'Toggle Full Screen',
          accelerator: 'Ctrl+Command+F',
          click: () => {
            this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
          }
        }
      ]
    }
    const subMenuWindow = {
      label: 'Window',
      submenu: [
        { label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' },
        { label: 'Close', accelerator: 'Command+W', selector: 'performClose:' },
        { type: 'separator' },
        { label: 'Bring All to Front', selector: 'arrangeInFront:' }
      ]
    }
    const subMenuHelp = {
      label: 'Help',
      submenu: [
        {
          label: 'Learn More',
          click() {
            shell.openExternal('https://zap.jackmallers.com/')
          }
        },
        {
          label: 'Documentation',
          click() {
            shell.openExternal('https://github.com/LN-Zap/zap-desktop')
          }
        },
        {
          label: 'Community Discussions',
          click() {
            shell.openExternal('zaphq.slack.com')
          }
        },
        {
          label: 'Search Issues',
          click() {
            shell.openExternal('https://github.com/LN-Zap/zap-desktop/issues')
          }
        }
      ]
    }

    const subMenuView = process.env.NODE_ENV === 'development' ? subMenuViewDev : subMenuViewProd

    return [
      subMenuAbout,
      subMenuEdit,
      subMenuView,
      subMenuWindow,
      subMenuHelp,
      this.buildLanguageMenu()
    ]
  }

  buildDefaultTemplate() {
    const templateDefault = [
      {
        label: '&File',
        submenu: [
          {
            label: '&Open',
            accelerator: 'Ctrl+O'
          },
          {
            label: '&Close',
            accelerator: 'Ctrl+W',
            click: () => {
              this.mainWindow.close()
            }
          }
        ]
      },
      {
        label: '&View',
        submenu:
          process.env.NODE_ENV === 'development'
            ? [
                {
                  label: '&Reload',
                  accelerator: 'Ctrl+R',
                  click: () => {
                    this.mainWindow.webContents.reload()
                  }
                },
                {
                  label: 'Toggle &Full Screen',
                  accelerator: 'F11',
                  click: () => {
                    this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
                  }
                },
                {
                  label: 'Toggle &Developer Tools',
                  accelerator: 'Alt+Ctrl+I',
                  click: () => {
                    this.mainWindow.toggleDevTools()
                  }
                }
              ]
            : [
                {
                  label: 'Toggle &Full Screen',
                  accelerator: 'F11',
                  click: () => {
                    this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
                  }
                }
              ]
      },
      {
        label: 'Help',
        submenu: [
          {
            label: 'Learn More',
            click() {
              shell.openExternal('https://zap.jackmallers.com/')
            }
          },
          {
            label: 'Documentation',
            click() {
              shell.openExternal('https://github.com/LN-Zap/zap-desktop')
            }
          },
          {
            label: 'Community Discussions',
            click() {
              shell.openExternal('zaphq.slack.com')
            }
          },
          {
            label: 'Search Issues',
            click() {
              shell.openExternal('https://github.com/LN-Zap/zap-desktop/issues')
            }
          },
          buildAboutMenu()
        ]
      },
      this.buildLanguageMenu()
    ]

    return templateDefault
  }

  buildLanguageMenu() {
    return {
      label: 'Language',
      submenu: locales.map(locale => {
        return {
          label: getLanguageName(locale),
          type: 'radio',
          checked: this.locale === locale,
          click: () => this.mainWindow.webContents.send('receiveLocale', locale)
        }
      })
    }
  }
}
<commit_msg>fix(wallet): resolve broken link in help menu<commit_after>// @flow
import os from 'os'
import { app, Menu, shell, BrowserWindow, ipcMain } from 'electron'
import openAboutWindow from './about'
import { getLanguageName, locales } from '../i18n'
import { getPackageDetails } from '../utils'

import { appRootPath } from '../lnd/util'

const path = require('path')

const buildAboutMenu = () => {
  return {
    label: 'About Zap',
    click: () => {
      const { productName, version } = getPackageDetails()
      openAboutWindow(
        {
          icon_path: path.resolve(appRootPath(), 'resources', 'icons', 'icon.png'),
          open_devtools: process.env.NODE_ENV !== 'production',
          product_name: `${productName} ${version}`
        },
        `file://${path.resolve(
          process.env.HOT ? 'app' : app.getAppPath(),
          'lib/zap/about/public/',
          'about.html'
        )}`
      )
    }
  }
}

export default class ZapMenuBuilder {
  mainWindow: BrowserWindow
  locale: string

  constructor(mainWindow: BrowserWindow) {
    this.mainWindow = mainWindow
    ipcMain.on('setLocale', (event, locale) => this.buildMenu(locale))
  }

  buildMenu(locale?: string) {
    if (locale) {
      this.locale = locale
    }

    let template
    if (os.platform() === 'darwin') {
      template = this.buildDarwinTemplate()
    } else {
      template = this.buildDefaultTemplate()
    }

    this.setupInputTemplate()

    const menu = Menu.buildFromTemplate(template)
    Menu.setApplicationMenu(menu)

    return menu
  }

  setupInputTemplate() {
    const selectionMenu = Menu.buildFromTemplate([
      { role: 'copy' },
      { type: 'separator' },
      { role: 'selectall' }
    ])

    const inputMenu = Menu.buildFromTemplate([
      { role: 'undo' },
      { role: 'redo' },
      { type: 'separator' },
      { role: 'cut' },
      { role: 'copy' },
      { role: 'paste' },
      { type: 'separator' },
      { role: 'selectall' }
    ])

    this.mainWindow.webContents.on('context-menu', (e, props) => {
      const { selectionText, isEditable } = props

      if (isEditable) {
        inputMenu.popup(this.mainWindow)
      } else if (selectionText && selectionText.trim() !== '') {
        selectionMenu.popup(this.mainWindow)
      }
    })
  }

  buildDarwinTemplate() {
    const subMenuAbout = {
      label: 'Electron',
      submenu: [
        buildAboutMenu(),
        { type: 'separator' },
        { label: 'Hide Zap', accelerator: 'Command+H', selector: 'hide:' },
        {
          label: 'Hide Others',
          accelerator: 'Command+Shift+H',
          selector: 'hideOtherApplications:'
        },
        { label: 'Show All', selector: 'unhideAllApplications:' },
        { type: 'separator' },
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: () => {
            app.quit()
          }
        }
      ]
    }
    const subMenuEdit = {
      label: 'Edit',
      submenu: [
        { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
        { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
        { type: 'separator' },
        { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
        { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
        { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
        { label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' }
      ]
    }
    const subMenuViewDev = {
      label: 'View',
      submenu: [
        {
          label: 'Reload',
          accelerator: 'Command+R',
          click: () => {
            this.mainWindow.webContents.reload()
          }
        },
        {
          label: 'Toggle Full Screen',
          accelerator: 'Ctrl+Command+F',
          click: () => {
            this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
          }
        },
        {
          label: 'Toggle Developer Tools',
          accelerator: 'Alt+Command+I',
          click: () => {
            this.mainWindow.toggleDevTools()
          }
        }
      ]
    }
    const subMenuViewProd = {
      label: 'View',
      submenu: [
        {
          label: 'Toggle Full Screen',
          accelerator: 'Ctrl+Command+F',
          click: () => {
            this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
          }
        }
      ]
    }
    const subMenuWindow = {
      label: 'Window',
      submenu: [
        { label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' },
        { label: 'Close', accelerator: 'Command+W', selector: 'performClose:' },
        { type: 'separator' },
        { label: 'Bring All to Front', selector: 'arrangeInFront:' }
      ]
    }
    const subMenuHelp = {
      label: 'Help',
      submenu: [
        {
          label: 'Learn More',
          click() {
            shell.openExternal('https://zap.jackmallers.com/')
          }
        },
        {
          label: 'Documentation',
          click() {
            shell.openExternal('https://github.com/LN-Zap/zap-desktop')
          }
        },
        {
          label: 'Community Discussions',
          click() {
            shell.openExternal('https://zaphq.slack.com')
          }
        },
        {
          label: 'Search Issues',
          click() {
            shell.openExternal('https://github.com/LN-Zap/zap-desktop/issues')
          }
        }
      ]
    }

    const subMenuView = process.env.NODE_ENV === 'development' ? subMenuViewDev : subMenuViewProd

    return [
      subMenuAbout,
      subMenuEdit,
      subMenuView,
      subMenuWindow,
      subMenuHelp,
      this.buildLanguageMenu()
    ]
  }

  buildDefaultTemplate() {
    const templateDefault = [
      {
        label: '&File',
        submenu: [
          {
            label: '&Open',
            accelerator: 'Ctrl+O'
          },
          {
            label: '&Close',
            accelerator: 'Ctrl+W',
            click: () => {
              this.mainWindow.close()
            }
          }
        ]
      },
      {
        label: '&View',
        submenu:
          process.env.NODE_ENV === 'development'
            ? [
                {
                  label: '&Reload',
                  accelerator: 'Ctrl+R',
                  click: () => {
                    this.mainWindow.webContents.reload()
                  }
                },
                {
                  label: 'Toggle &Full Screen',
                  accelerator: 'F11',
                  click: () => {
                    this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
                  }
                },
                {
                  label: 'Toggle &Developer Tools',
                  accelerator: 'Alt+Ctrl+I',
                  click: () => {
                    this.mainWindow.toggleDevTools()
                  }
                }
              ]
            : [
                {
                  label: 'Toggle &Full Screen',
                  accelerator: 'F11',
                  click: () => {
                    this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen())
                  }
                }
              ]
      },
      {
        label: 'Help',
        submenu: [
          {
            label: 'Learn More',
            click() {
              shell.openExternal('https://zap.jackmallers.com/')
            }
          },
          {
            label: 'Documentation',
            click() {
              shell.openExternal('https://github.com/LN-Zap/zap-desktop')
            }
          },
          {
            label: 'Community Discussions',
            click() {
              shell.openExternal('zaphq.slack.com')
            }
          },
          {
            label: 'Search Issues',
            click() {
              shell.openExternal('https://github.com/LN-Zap/zap-desktop/issues')
            }
          },
          buildAboutMenu()
        ]
      },
      this.buildLanguageMenu()
    ]

    return templateDefault
  }

  buildLanguageMenu() {
    return {
      label: 'Language',
      submenu: locales.map(locale => {
        return {
          label: getLanguageName(locale),
          type: 'radio',
          checked: this.locale === locale,
          click: () => this.mainWindow.webContents.send('receiveLocale', locale)
        }
      })
    }
  }
}
<|endoftext|>


<commit_before>/*
 * CARIS oscar - Open Spatial Component ARchitecture
 *
 * Copyright 2012 CARIS <http://www.caris.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Class: oscar.Control.OverviewMap
 *
 * The Oscar OverviewMap control creates a small overview map which displays
 * a zoomed extent of the main map.
 *
 * Inherits from:
 * - <OpenLayers.Control.OverviewMap>
 */

oscar.Control.OverviewMap= oscar.BaseClass(OpenLayers.Control.OverviewMap, {
    /**
     * Constructor: oscar.Control.OverviewMap
     *
     * Parameters:
     * options - {Object} An optional object whose properties will be set on
	 *           this instance.
     */
    initialize: function(options) {
        OpenLayers.Control.OverviewMap.prototype.initialize.apply(this, arguments);
	},
    /**
     * APIMethod: draw
     * Renders the OverviewMap in the browser.
     */
    draw: function() {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        if(!(this.layers.length > 0)) {
            if (this.map.baseLayer) {
                var layer = this.map.baseLayer.clone();
                this.layers = [layer];
            } else {
                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
                return this.div;
            }
        }

        // create overview map DOM elements
        this.element = document.createElement('div');
        this.element.className = this.displayClass + 'Element';
        this.element.style.display = 'none';

        this.mapDiv = document.createElement('div');
        this.mapDiv.style.width = this.size.w + 'px';
        this.mapDiv.style.height = this.size.h + 'px';
        this.mapDiv.style.position = 'relative';
        this.mapDiv.style.overflow = 'hidden';
        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');

        this.extentRectangle = document.createElement('div');
        this.extentRectangle.style.position = 'absolute';
        this.extentRectangle.style.zIndex = 1000;  //HACK
        this.extentRectangle.className = this.displayClass+'ExtentRectangle';
        this.mapDiv.appendChild(this.extentRectangle);

        this.element.appendChild(this.mapDiv);

        this.div.appendChild(this.element);

        // Optionally add min/max buttons if the control will go in the
        // map viewport.
        if(!this.outsideViewport) {
            this.div.className += " " + this.displayClass + 'Container';
            var imgLocation = oscar._imgPath;
            var sz = new OpenLayers.Size(21,21);
            // maximize button div
            var img = oscar.getImagePath() + 'overviewWindow_expand.png';
            this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
                                        "OpenLayers_Control_MaximizeDiv",
                                        null,
                                        sz,
                                        img,
                                        "absolute");
            this.maximizeDiv.style.display = 'none';
            this.maximizeDiv.className = this.displayClass + 'MaximizeButton';
            OpenLayers.Event.observe(this.maximizeDiv, 'click',
                OpenLayers.Function.bindAsEventListener(this.maximizeControl,
                                                        this)
            );
            this.div.appendChild(this.maximizeDiv);

            // minimize button div
            var img = oscar.getImagePath() + 'overviewWindow_collapse.png';
            var sz = new OpenLayers.Size(21,21);
            this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
                                        "OpenLayers_Control_MinimizeDiv",
                                        null,
                                        sz,
                                        img,
                                        "absolute");
            this.minimizeDiv.style.display = 'none';
            this.minimizeDiv.className = this.displayClass + 'MinimizeButton';
            OpenLayers.Event.observe(this.minimizeDiv, 'click',
                OpenLayers.Function.bindAsEventListener(this.minimizeControl,
                                                        this)
            );
            this.div.appendChild(this.minimizeDiv);

            var eventsToStop = ['dblclick','mousedown'];

            for (var i=0, len=eventsToStop.length; i<len; i++) {

                OpenLayers.Event.observe(this.maximizeDiv,
                                         eventsToStop[i],
                                         OpenLayers.Event.stop);

                OpenLayers.Event.observe(this.minimizeDiv,
                                         eventsToStop[i],
                                         OpenLayers.Event.stop);
            }

            this.minimizeControl();
        } else {
            // show the overview map
            this.element.style.display = '';
        }
        if(this.map.getExtent()) {
            this.update();
        }

        this.map.events.register('moveend', this, this.update);

        return this.div;
    },

    /**
     * Constant: CLASS_NAME
     * - oscar.Control.OverviewMap
     */
	CLASS_NAME:"oscar.Control.OverviewMap"
});<commit_msg>SFV-1125<commit_after>/*
 * CARIS oscar - Open Spatial Component ARchitecture
 *
 * Copyright 2012 CARIS <http://www.caris.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Class: oscar.Control.OverviewMap
 *
 * The Oscar OverviewMap control creates a small overview map which displays
 * a zoomed extent of the main map.
 *
 * Inherits from:
 * - <OpenLayers.Control.OverviewMap>
 */

oscar.Control.OverviewMap= oscar.BaseClass(OpenLayers.Control.OverviewMap, {
    /**
     * Constructor: oscar.Control.OverviewMap
     *
     * Parameters:
     * options - {Object} An optional object whose properties will be set on
	 *           this instance.
     */
    initialize: function(options) {
        OpenLayers.Control.OverviewMap.prototype.initialize.apply(this, arguments);
	},
    setMap:function(map) {
        OpenLayers.Control.prototype.setMap.apply(this,[map]);

        this.map.events.register("addlayer",this,function(evt){
            console.log(evt.layer);
        });

    },
    draw:function() {
        var layers = [];
        for(var l in this.map.layers) {
            if(this.map.layers[l].renderer) continue;
            if(this.map.layers[l].clone) {
                layers.push(this.map.layers[l].clone());
            }
        }
        this.layers = layers;
        OpenLayers.Control.OverviewMap.prototype.draw.apply(this,arguments);
        $$(this.maximizeDiv).empty();
        $$(this.minimizeDiv).empty();
        return this.div;
    },

    /**
     * Constant: CLASS_NAME
     * - oscar.Control.OverviewMap
     */
	CLASS_NAME:"oscar.Control.OverviewMap"
});<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';

export default Ember.Mixin.create({

    /// <param name="error">
    ///     DS.InvalidError
    ///     jqXHR
    ///     string
    ///     array of strings
    /// </param>
    /// <returns type="array" />
    getErrorMessages: function(error) {

        // When rejected after calling record.save().then(resolve, reject), it's easiest
        // to pass in the reason parameter as the error object for this function.
        // if server returns 422, then the reason parameter will be DS.InvalidError, otherwise, it will be jqXHR

        // When substate goes into error, easiest just to pass in the error substate model.
        // if server returns 422, then the model will be DS.InvalidError, otherwise, it will be jqXHR

        var fallbackMessages = ['There was an unexpected error.'];

        if (Ember.typeOf(error) === 'undefined')
            return fallbackMessages;

        if (Ember.typeOf(error) === 'null')
                return fallbackMessages;

        if (Ember.typeOf(error) === 'string')
            return [ error ];

        if (Ember.typeOf(error) === 'array')
            return error;

        // interestingly, DS.InvalidError contains all of the error messages, even for the attributes (e.g. base) that don't exist in the model.
        // DS.Errors, which is attached to the model as record.get('errors'), only contains the errors for the attributes that are on the model. http://stackoverflow.com/a/24031233/188740
        if (error instanceof DS.InvalidError)
            return this._messagesFromObject(error.errors);

        if (Ember.typeOf(error.responseJSON) === 'object' && Ember.typeOf(error.responseJSON.errors) === 'object')
            return this._messagesFromObject(error.responseJSON.errors);

        if (Ember.typeOf(error.message) === 'string')
            return [ error.message ];

        return fallbackMessages;
    },

    _messagesFromObject: function(object) {

        /*jshint loopfunc: true */

        var messages = [];
        for (var key in object) {
            var value = object[key];
            if(Ember.typeOf(value) === 'array')
                value.forEach(
                    function(message) {
                        messages.push(this._concatenateMessage(key, message));
                    }.bind(this)
                );
            else
                messages.push(this._concatenateMessage(key, value));
        }

        return messages;
    },

    _concatenateMessage: function(key, value) {
        if (key === 'base')
            return value;
        else
            return this._titleize(key) + ' ' + value;
    },

    _titleize: function(s) {
        return s.underscore().replace(/_/g, ' ').capitalize();
    }

});
<commit_msg>Added extra error parsing condition<commit_after>import Ember from 'ember';
import DS from 'ember-data';

export default Ember.Mixin.create({

    /// <param name="error">
    ///     DS.InvalidError
    ///     jqXHR
    ///     string
    ///     array of strings
    /// </param>
    /// <returns type="array" />
    getErrorMessages: function(error) {

        // When rejected after calling record.save().then(resolve, reject), it's easiest
        // to pass in the reason parameter as the error object for this function.
        // if server returns 422, then the reason parameter will be DS.InvalidError, otherwise, it will be jqXHR

        // When substate goes into error, easiest just to pass in the error substate model.
        // if server returns 422, then the model will be DS.InvalidError, otherwise, it will be jqXHR

        var fallbackMessages = ['There was an unexpected error.'];

        if (Ember.typeOf(error) === 'undefined')
            return fallbackMessages;

        if (Ember.typeOf(error) === 'null')
                return fallbackMessages;

        if (Ember.typeOf(error) === 'string')
            return [ error ];

        if (Ember.typeOf(error) === 'array')
            return error;

        // interestingly, DS.InvalidError contains all of the error messages, even for the attributes (e.g. base) that don't exist in the model.
        // DS.Errors, which is attached to the model as record.get('errors'), only contains the errors for the attributes that are on the model. http://stackoverflow.com/a/24031233/188740
        if (error instanceof DS.InvalidError)
            return this._messagesFromObject(error.errors);

        if (Ember.typeOf(error.responseJSON) === 'object' && Ember.typeOf(error.responseJSON.errors) === 'object')
            return this._messagesFromObject(error.responseJSON.errors);

        if (Ember.typeOf(error.jqXHR) === 'object' && Ember.typeOf(error.jqXHR.responseJSON) === 'object' && Ember.typeOf(error.jqXHR.responseJSON.errors) === 'object')
            return this._messagesFromObject(error.jqXHR.responseJSON.errors);

        if (Ember.typeOf(error.message) === 'string')
            return [ error.message ];

        return fallbackMessages;
    },

    _messagesFromObject: function(object) {

        /*jshint loopfunc: true */

        var messages = [];
        for (var key in object) {
            var value = object[key];
            if(Ember.typeOf(value) === 'array')
                value.forEach(
                    function(message) {
                        messages.push(this._concatenateMessage(key, message));
                    }.bind(this)
                );
            else
                messages.push(this._concatenateMessage(key, value));
        }

        return messages;
    },

    _concatenateMessage: function(key, value) {
        if (key === 'base')
            return value;
        else
            return this._titleize(key) + ' ' + value;
    },

    _titleize: function(s) {
        return s.underscore().replace(/_/g, ' ').capitalize();
    }

});
<|endoftext|>


<commit_before>Ext.define('doweown.model.WorldCatBooks', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
                {name: 'title',         type: 'string', mapping: 'title'},
                {name: 'author',        type: 'string', mapping: 'author'},
                {name: 'publisher',     type: 'string', mapping: 'publisher'},
		{name: 'date',          type: 'string', mapping: 'date'},
		{name: 'thumbnail',     type: 'string', mapping: 'thumbnail', defaultValue: doweown.config.Config.getNoThumbnailUrl() },
		{name: 'ISBN', mapping: 'ISBN'},
		{name: 'totalLibCount',	type: 'string', mapping: 'totalLibCount'},
		{name: 'borrowDirectUrl', type: 'string', defaultValue: doweown.config.Config.getBorrowDirectDefaultUrl() },
		{name: 'library', mapping: 'library'},
		{name: 'description', type: 'string'},
		{name: 'mailto', type: 'string'}
        ]
    }
});
<commit_msg>changed totalLibCount to int field<commit_after>Ext.define('doweown.model.WorldCatBooks', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
                {name: 'title',         type: 'string', mapping: 'title'},
                {name: 'author',        type: 'string', mapping: 'author'},
                {name: 'publisher',     type: 'string', mapping: 'publisher'},
		{name: 'date',          type: 'string', mapping: 'date'},
		{name: 'thumbnail',     type: 'string', mapping: 'thumbnail', defaultValue: doweown.config.Config.getNoThumbnailUrl() },
		{name: 'ISBN', mapping: 'ISBN'},
		{name: 'totalLibCount',	type: 'int', mapping: 'totalLibCount'},
		{name: 'borrowDirectUrl', type: 'string', defaultValue: doweown.config.Config.getBorrowDirectDefaultUrl() },
		{name: 'library', mapping: 'library'},
		{name: 'description', type: 'string'},
		{name: 'mailto', type: 'string'}
        ]
    }
});
<|endoftext|>


<commit_before>import {Map} from 'immutable';
import {base64} from '@ciscospark/common';
import {metrics} from '@ciscospark/react-component-utils';

export const initialState = new Map({
  currentUser: null,
  toUser: null,
  status: new Map({
    isFetchingCurrentUser: false,
    isFetchingToUser: false
  })
});

export const GET_CURRENT_USER = `user/GET_CURRENT_USER`;
export const GET_CURRENT_USER_BEGIN = `user/GET_CURRENT_USER_BEGIN`;
export const GET_TO_USER = `user/GET_TO_USER`;
export const GET_TO_USER_BEGIN = `user/GET_TO_USER_BEGIN`;
export default function reducer(state = initialState, action) {
  switch (action.type) {
  case GET_CURRENT_USER:
    return state.setIn([`status`, `isFetchingCurrentUser`], action.payload.isFetching)
      .set(`currentUser`, action.payload.user);
  case GET_TO_USER: {
    const user = action.payload.user || {};
    if (user && user.id) {
      user.id = base64.decode(action.payload.user.id).split(`/`).pop();
    }
    return state.setIn([`status`, `isFetchingToUser`], action.payload.isFetching)
      .set(`toUser`, user);
  }
  case GET_CURRENT_USER_BEGIN:
    return state.setIn([`status`, `isFetchingCurrentUser`], action.payload.isFetching);
  case GET_TO_USER_BEGIN:
    return state.setIn([`status`, `isFetchingToUser`], action.payload.isFetching);

  default:
    return state;
  }
}

export function getCurrentUser(user) {
  return {
    type: GET_CURRENT_USER,
    payload: {
      isFetching: false,
      user
    }
  };
}

export function getToUser(user) {
  return {
    type: GET_TO_USER,
    payload: {
      isFetching: false,
      user
    }
  };
}

export function getCurrentUserBegin() {
  return {
    type: GET_CURRENT_USER_BEGIN,
    payload: {
      isFetching: true
    }
  };
}

export function getToUserBegin() {
  return {
    type: GET_TO_USER_BEGIN,
    payload: {
      isFetching: true
    }
  };
}

export function fetchCurrentUser(spark) {
  return (dispatch) => {
    dispatch(getCurrentUserBegin());
    return spark.user.get()
      .then((user) => {
        dispatch(getCurrentUser(user));
      });
  };
}

const metricName = {
  FETCH_TO_USER_START: `fetchToUser:start`,
  FETCH_TO_USER_END: `fetchToUser:end`,
  FETCH_TO_USER_DURATION: `fetchToUser:duration`
};

export function fetchToUser({toPersonEmail, toPersonId}, spark) {
  return (dispatch) => {
    metrics.saveElapsedTime(metricName.FETCH_TO_USER_START);
    metrics.sendTempMetric(metricName.FETCH_TO_USER_START, spark);
    dispatch(getToUserBegin());
    let req;
    if (toPersonEmail) {
      req = {
        service: `hydra`,
        resource: `/people`,
        qs: {
          email: toPersonEmail
        }
      };
    }
    else if (toPersonId) {
      req = {
        service: `hydra`,
        resource: `/people/${toPersonId}`
      };
    }
    return spark.request(req).then((res) => {
      metrics.saveElapsedTime(metricName.FETCH_TO_USER_END);
      metrics.sendTempMetric(metricName.FETCH_TO_USER_END, spark);
      metrics.sendDurationMetric(
        metricName.FETCH_TO_USER_DURATION,
        metricName.FETCH_TO_USER_START,
        metricName.FETCH_TO_USER_END,
        spark
      );
      dispatch(getToUser(res.body.items[0]));
    });
  };
}
<commit_msg>feat(redux-module-user): convert request to use people plugin<commit_after>import {Map} from 'immutable';
import {base64} from '@ciscospark/common';
import {metrics} from '@ciscospark/react-component-utils';

export const initialState = new Map({
  currentUser: null,
  toUser: null,
  status: new Map({
    isFetchingCurrentUser: false,
    isFetchingToUser: false
  })
});

export const GET_CURRENT_USER = `user/GET_CURRENT_USER`;
export const GET_CURRENT_USER_BEGIN = `user/GET_CURRENT_USER_BEGIN`;
export const GET_TO_USER = `user/GET_TO_USER`;
export const GET_TO_USER_BEGIN = `user/GET_TO_USER_BEGIN`;
export default function reducer(state = initialState, action) {
  switch (action.type) {
  case GET_CURRENT_USER:
    return state.setIn([`status`, `isFetchingCurrentUser`], action.payload.isFetching)
      .set(`currentUser`, action.payload.user);
  case GET_TO_USER: {
    const user = action.payload.user || {};
    if (user && user.id) {
      user.id = base64.decode(action.payload.user.id).split(`/`).pop();
    }
    return state.setIn([`status`, `isFetchingToUser`], action.payload.isFetching)
      .set(`toUser`, user);
  }
  case GET_CURRENT_USER_BEGIN:
    return state.setIn([`status`, `isFetchingCurrentUser`], action.payload.isFetching);
  case GET_TO_USER_BEGIN:
    return state.setIn([`status`, `isFetchingToUser`], action.payload.isFetching);

  default:
    return state;
  }
}

export function getCurrentUser(user) {
  return {
    type: GET_CURRENT_USER,
    payload: {
      isFetching: false,
      user
    }
  };
}

export function getToUser(user) {
  return {
    type: GET_TO_USER,
    payload: {
      isFetching: false,
      user
    }
  };
}

export function getCurrentUserBegin() {
  return {
    type: GET_CURRENT_USER_BEGIN,
    payload: {
      isFetching: true
    }
  };
}

export function getToUserBegin() {
  return {
    type: GET_TO_USER_BEGIN,
    payload: {
      isFetching: true
    }
  };
}

export function fetchCurrentUser(spark) {
  return (dispatch) => {
    dispatch(getCurrentUserBegin());
    return spark.user.get()
      .then((user) => {
        dispatch(getCurrentUser(user));
      });
  };
}

const metricName = {
  FETCH_TO_USER_START: `fetchToUser:start`,
  FETCH_TO_USER_END: `fetchToUser:end`,
  FETCH_TO_USER_DURATION: `fetchToUser:duration`
};

export function fetchToUser({toPersonEmail, toPersonId}, spark) {
  return (dispatch) => {
    metrics.saveElapsedTime(metricName.FETCH_TO_USER_START);
    metrics.sendTempMetric(metricName.FETCH_TO_USER_START, spark);
    dispatch(getToUserBegin());
    const handleResponse = (person) => {
      metrics.saveElapsedTime(metricName.FETCH_TO_USER_END);
      metrics.sendTempMetric(metricName.FETCH_TO_USER_END, spark);
      metrics.sendDurationMetric(
        metricName.FETCH_TO_USER_DURATION,
        metricName.FETCH_TO_USER_START,
        metricName.FETCH_TO_USER_END,
        spark
      );
      dispatch(getToUser(person));
    };
    if (toPersonEmail) {
      return spark.people.list({email: toPersonEmail})
      .then((res) => res.items[0])
      .then(handleResponse);
    }
    else if (toPersonId) {
      return spark.people.get(toPersonId).then(handleResponse);
    }
    return Promise.reject(new Error(`toPersonEmail or toPersonId is required`));
  };
}
<|endoftext|>


<commit_before>angular.module('Board', [])
.factory('TileModel', function() {
	var Tile = function(pos, val) {
		this.x = pos.x;
		this.y = pos.y;
		this.value = val || 2;
	};
	return Tile;
})
.service('BoardService', function(TileModel) {
	this.buildEmptyGameBoard = function() {
      var self = this;
      // Initialize our board
      for (var x = 0; x < service.size * service.size; x++) {
        this.board[x] = null;
      }

      this.forEach(function(x,y) {
        self.setCellAt({x:x,y:y}, null);
      });
    };
	this.board = [];
	this.tiles = [];
	this.tiles.push(new TileModel({x: 1, y: 1}, 2));
  	this.tiles.push(new TileModel({x: 1, y: 2}, 2));
	this.size = 4;

	this.forEach = function(cb) {
		var totalSize = this.size * this.size;
		for (var i = 0; i < totalSize; i++) {
			var pos = this._positionToCoordinates(i);
			cb(pos.x, pos.y, this.tiles[i]);
		}
	};

// Set a cell at position
	this.setCellAt = function(pos, tile) {
	if (this.withinGrid(pos)) {
		var xPos = this._coordinatesToPosition(pos);
		this.tiles[xPos] = tile;
		}
	};

// Fetch a cell at a given position
	this.getCellAt = function(pos) {
		if (this.withinGrid(pos)) {
			var x = this._coordinatesToPosition(pos);
			return this.tiles[x];
		} else {
			return null;
		}
	};

// A small helper function to determine if a position is
// within the boundaries of our grid
	this.withinGrid = function(cell) {
		return cell.x >= 0 && cell.x < this.size &&
		cell.y >= 0 && cell.y < this.size;
	};
});<commit_msg>add initial tile positions<commit_after>angular.module('Board', [])
.factory('TileModel', function() {
	var Tile = function(pos, val) {
		this.x = pos.x;
		this.y = pos.y;
		this.value = val || 2;
	};
	return Tile;
})
.service('BoardService', function(TileModel) {
	this.buildEmptyGameBoard = function() {
      var self = this;
      // Initialize our board
      for (var x = 0; x < service.size * service.size; x++) {
        this.board[x] = null;
      }

      this.forEach(function(x,y) {
        self.setCellAt({x:x,y:y}, null);
      });
    };
	this.board = [];
	this.tiles = [];
	this.tiles.push(new TileModel({x: 1, y: 1}, 2));
  	this.tiles.push(new TileModel({x: 1, y: 2}, 2));
	this.size = 4;

	this.forEach = function(cb) {
		var totalSize = this.size * this.size;
		for (var i = 0; i < totalSize; i++) {
			var pos = this._positionToCoordinates(i);
			cb(pos.x, pos.y, this.tiles[i]);
		}
	};

// Set a cell at position
	this.setCellAt = function(pos, tile) {
	if (this.withinBoard(pos)) {
		var xPos = this._coordinatesToPosition(pos);
		this.tiles[xPos] = tile;
		}
	};

// Fetch a cell at a given position
	this.getCellAt = function(pos) {
		if (this.withinBoard(pos)) {
			var x = this._coordinatesToPosition(pos);
			return this.tiles[x];
		} else {
			return null;
		}
	};

// A small helper function to determine if a position is
// within the boundaries of our Board
	this.withinBoard = function(cell) {
		return cell.x >= 0 && cell.x < this.size &&
		cell.y >= 0 && cell.y < this.size;
	};
<<<<<<< Updated upstream
=======
	this._positionToCoordinates = function(i) {
		var x = i % service.size,
	    y = (i - x) / service.size;
	    return {
		    x: x,
		    y: y
	    };
	};

// Helper to convert coordinates to position
	this._coordinatesToPosition = function(pos) {
		return (pos.y * service.size) + pos.x;
	};

  	this.startingTileNumber = 2;
  	this.buildStartingPosition = function() {
    	for (var x = 0; x < this.startingTileNumber; x++) {
      		this.randomlyInsertNewTile();
      	}
    }
    this.availableCells = function() {
    var cells = [],
        self = this;

    this.forEach(function(x,y) {
      var foundTile = self.getCellAt({x:x, y:y});
      if (!foundTile) {
        cells.push({x:x,y:y});
      }
    });

    return cells;
  };
>>>>>>> Stashed changes
});<|endoftext|>


<commit_before>'use strict';

var angular = require('angular');

function RootCtrl($scope, $location, $timeout, KeyboardShortcutManager, QueueManager, NotificationCentersRegistry,
                  Orchestrator, UserInteractionManager, QueuesRegistry, ModalManager, PointerManager, Capabilities,
                  SearchCtrlHelper) {

  var rootCtrl = this;

  /**
   * Stores the serialized version of the queue. Useful to check the new url state against the internal state
   * to prevent infinite loops when changing the url internally.
   *
   * @type {string}
   */
  var serializedQueue;

  rootCtrl.queueLoading = false;

  rootCtrl.isSearchShown = isSearchShown;
  rootCtrl.getQueue = getQueue;
  rootCtrl.getRunningQueueEntry = getRunningQueueEntry;
  rootCtrl.getLoadingQueueEntry = getLoadingQueueEntry;
  rootCtrl.isPlaying = isPlaying;
  rootCtrl.shouldIdleChrome = shouldIdleChrome;
  rootCtrl.isMouseDetected = isMouseDetected;
  rootCtrl.shouldShowScene = shouldShowScene;
  rootCtrl.shouldShowPlaybackControls = shouldShowPlaybackControls;
  rootCtrl.toggleSearch = SearchCtrlHelper.toggleSearch;
  rootCtrl.togglePlayback = togglePlayback;

  // setup direct access to the property for double binding
  Object.defineProperty(rootCtrl, 'searchTerm', {
    get: function() {
      return SearchCtrlHelper.searchTerm;
    },
    set: function(value) {
      SearchCtrlHelper.searchTerm = value;
    }
  });

  function isSearchShown() {
    return SearchCtrlHelper.searchShown;
  }

  function getQueue() {
    return QueueManager.queue;
  }

  function getRunningQueueEntry() {
    return Orchestrator.runningQueueEntry;
  }

  function getLoadingQueueEntry() {
    return Orchestrator.loadingQueueEntry;
  }

  function isPlaying() {
    return Orchestrator.playing;
  }

  function shouldIdleChrome() {
    return !UserInteractionManager.userInteracting;
  }

  function isMouseDetected() {
    return PointerManager.mouseDetected;
  }

  function shouldShowScene() {
    return Capabilities.playback;
  }

  function shouldShowPlaybackControls() {
    return Capabilities.playback || Capabilities.remoteControl;
  }

  function togglePlayback() {
    Orchestrator.togglePlayback();
  }

  // we need to wait for the loading phase to be done to avoid race problems (loading for ever)
  $timeout(activate);

  function activate() {
    // hide the input search at startup
    SearchCtrlHelper.toggleSearch(false);

    // register the global space shortcut
    KeyboardShortcutManager.register('space', function(evt) {
      evt.preventDefault();
      Orchestrator.togglePlayback();
    });

    KeyboardShortcutManager.register('search', 'esc', function(evt) {
      evt.preventDefault();
      SearchCtrlHelper.toggleSearch(false);
    });

    $scope.$watch(function() {
      return QueueManager.queue;
    }, function(newVal, oldVal) {
      // this test is here to prevent to serialize during the init phase
      if (newVal !== oldVal) {
        var newSerializedQueue = QueueManager.serialize();
        if (serializedQueue !== newSerializedQueue) {
          serializedQueue = newSerializedQueue;
          // replace queue parameter but keep the rest
          $location.search(angular.extend({}, $location.search(), {queue: serializedQueue}));
        }
      }
    }, true);

    $scope.$watch(function() {
      return $location.search().queue;
    }, function(newSerializedQueue) {
      if (serializedQueue !== newSerializedQueue) {
        serializedQueue = newSerializedQueue;
        // change initiated by user (back / forward etc.), need to be deserialized
        rootCtrl.queueLoading = true;
        QueueManager.deserialize(serializedQueue).catch(function(message) {
          NotificationCentersRegistry('notificationCenter').ready(function(notificationCenter) {
            notificationCenter.error(message);
          });
        }).finally(function() {
          rootCtrl.queueLoading = false;
        });
      }
    });

    $scope.$watch(function() {
      return Orchestrator.runningQueueEntry;
    }, function(runningQueueEntry, oldVal) {
      if (runningQueueEntry !== oldVal) {
        QueuesRegistry('queue').ready(function(queue) {
          queue.focusEntry(runningQueueEntry);
        });
      }
    });

    $scope.$watch(function() {
      return Capabilities.playback;
    }, function(playback) {
      if (playback === false) {
        ModalManager.open({
          title: 'MixTube won\'t work on your device',
          contentTemplateUrl: '/scripts/components/capabilities/no-playback-modal-content.html',
          commands: [{label: 'OK', primary: true}]
        });
      }
    });
  }
}

function SearchResultsCtrl($scope, $timeout, YoutubeClient, SearchCtrlHelper) {

  var searchResultsCtrl = this;

  /**
   * @const
   * @type {number}
   */
  var INSTANT_SEARCH_DELAY = 500;

  /** @type {number} */
  var searchRequestCount = 0;
  /** @type {?Promise} */
  var instantSearchPromise = null;

  // the following variables will be initialized by the initSearch function.

  /**
   * The user already executed one search. Used to hide the results pane until there is something to show.
   *
   * @type {boolean}
   */
  searchResultsCtrl.inSearch = null;
  /**
   * A list of results pages.
   *
   * @type {Object.<string, Array.<Array.<Video>>>}
   */
  searchResultsCtrl.results = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.pending = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.pendingMore = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.error = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.noneFound = null;
  /** @type {Object.<string, string>} */
  searchResultsCtrl.nextPageId = null;

  searchResultsCtrl.getSearchTerm = getSearchTerm;
  searchResultsCtrl.shouldShowSearchResultPanel = shouldShowSearchResultPanel;
  searchResultsCtrl.showMore = showMore;

  activate();

  function getSearchTerm() {
    return SearchCtrlHelper.searchTerm;
  }

  function shouldShowSearchResultPanel() {
    return SearchCtrlHelper.searchShown && searchResultsCtrl.inSearch;
  }

  function initSearch() {
    instantSearchPromise = null;
    searchResultsCtrl.inSearch = false;
    searchResultsCtrl.results = {youtube: [[]]};
    searchResultsCtrl.pending = {youtube: false};
    searchResultsCtrl.pendingMore = {youtube: false};
    searchResultsCtrl.nextPageId = {youtube: null};
    searchResultsCtrl.error = {youtube: false};
    searchResultsCtrl.noneFound = {youtube: false};
  }

  function showMore(pId, nextPageId) {
    if (pId === 'youtube') {
      // clear any error message (case of retry after error)
      searchResultsCtrl.error.youtube = false;
      searchYoutube(SearchCtrlHelper.searchTerm, nextPageId);
    }
  }

  /**
   * @param {string} term
   * @param {string=} nextPageId
   * @returns {Promise}
   */
  function searchYoutube(term, nextPageId) {
    var first = !nextPageId;

    var startSearchRequestCount = searchRequestCount;

    if (first) {
      searchResultsCtrl.pending.youtube = true;

      // reset the results list and the next page token since we are starting a new search
      searchResultsCtrl.results.youtube = [];
      searchResultsCtrl.nextPageId.youtube = null;
    } else {
      searchResultsCtrl.pendingMore.youtube = true;
    }

    return YoutubeClient.searchVideosByQuery(term,
      {pageSize: first ? 11 : 12, pageId: nextPageId}).then(function doneCb() {
        if (searchRequestCount === startSearchRequestCount) {
          if (first) {
            searchResultsCtrl.pending.youtube = false;
          } else {
            searchResultsCtrl.pendingMore.youtube = false;
          }
        }
      }, null, function progressCb(results) {
        if (searchRequestCount === startSearchRequestCount) {
          if (results.videos.length) {
            searchResultsCtrl.results.youtube.push(results.videos);
            searchResultsCtrl.nextPageId.youtube = results.nextPageId;
          } else {
            searchResultsCtrl.noneFound.youtube = true;
          }
        }
      }).catch(function catchCb() {
        if (searchRequestCount === startSearchRequestCount) {
          searchResultsCtrl.error.youtube = true;
          if (first) {
            searchResultsCtrl.pending.youtube = false;
            searchResultsCtrl.results.youtube = [];
          } else {
            searchResultsCtrl.pendingMore.youtube = false;
          }
        }
      });
  }

  function activate() {
    initSearch();

    // when the user types we automatically execute the search
    $scope.$watch(function() {
      return SearchCtrlHelper.searchTerm;
    }, function(newSearchTerm) {
      if (newSearchTerm !== null) {

        // new inputs so we stop the previous request
        $timeout.cancel(instantSearchPromise);

        // as soon as the query changes clear messages
        searchResultsCtrl.error.youtube = false;
        searchResultsCtrl.noneFound.youtube = false;

        // if the search has to be longer than two characters
        if (newSearchTerm.length > 2) {
          searchRequestCount++;

          $timeout.cancel(instantSearchPromise);
          instantSearchPromise = $timeout(function search() {
            searchResultsCtrl.inSearch = true;
            searchYoutube(newSearchTerm);
          }, INSTANT_SEARCH_DELAY);
        }
      }
    });

    // ensures everything is initialized when the search is shown and stopped when it is hidden
    $scope.$watch(function() {
      return SearchCtrlHelper.searchShown;
    }, function(searchShown) {
      if (searchShown) {
        initSearch();
      } else {
        $timeout.cancel(instantSearchPromise);
      }
    });
  }
}

function SearchResultCtrl($timeout, QueueManager, QueuesRegistry, Orchestrator) {

  var searchResultCtrl = this;

  /**
   * @const
   * @type {number}
   */
  var CONFIRMATION_DURATION = 4000;

  /** @type {?Promise} */
  var tmoPromise = null;

  /** @type {boolean} */
  searchResultCtrl.shouldShowConfirmation = false;
  /** @type {?number} */
  searchResultCtrl.countBeforePlayback = null;

  searchResultCtrl.appendResultToQueue = appendResultToQueue;

  /**
   * @param {mt.Video} video
   */
  function appendResultToQueue(video) {

    var queueEntry = QueueManager.appendVideo(video);

    if (Orchestrator.runningQueueEntry) {
      var entries = QueueManager.queue.entries;
      searchResultCtrl.countBeforePlayback = entries.indexOf(queueEntry) - entries.indexOf(Orchestrator.runningQueueEntry);
    } else {
      searchResultCtrl.countBeforePlayback = null;
    }

    QueuesRegistry('queue').ready(function(queue) {
      queue.focusEntry(queueEntry);
    });

    searchResultCtrl.shouldShowConfirmation = true;
    $timeout.cancel(tmoPromise);
    tmoPromise = $timeout(function() {
      searchResultCtrl.shouldShowConfirmation = false;
    }, CONFIRMATION_DURATION);
  }
}

function QueueCtrl(Orchestrator, QueueManager) {

  var queueCtrl = this;

  queueCtrl.playQueueEntry = playQueueEntry;
  queueCtrl.removeQueueEntry = removeQueueEntry;

  /**
   * @param {number} queueIndex
   */
  function playQueueEntry(queueIndex) {
    Orchestrator.skipTo(queueIndex);
  }

  /**
   * @param {mt.QueueEntry} queueEntry
   */
  function removeQueueEntry(queueEntry) {
    QueueManager.removeEntry(queueEntry);
  }
}

function DebuggingCtrl(Configuration, KeyboardShortcutManager, NotificationCentersRegistry) {

  activate();

  function notification(message) {
    NotificationCentersRegistry('notificationCenter').ready(function(notificationCenter) {
      notificationCenter.error(message);
    });
  }

  function activate() {
    if (Configuration.debugNotifications) {
      // register the global space shortcut
      KeyboardShortcutManager.register('ctrl+n', function(evt) {
        evt.preventDefault();
        notification('Debugging: Test notification');
      });
    }
  }
}

exports.RootCtrl = RootCtrl;
exports.SearchResultsCtrl = SearchResultsCtrl;
exports.SearchResultCtrl = SearchResultCtrl;
exports.QueueCtrl = QueueCtrl;
exports.DebuggingCtrl = DebuggingCtrl;<commit_msg># Backspace bug prevented the backspace shortcut<commit_after>'use strict';

var angular = require('angular');

function RootCtrl($scope, $location, $timeout, KeyboardShortcutManager, QueueManager, NotificationCentersRegistry,
                  Orchestrator, UserInteractionManager, QueuesRegistry, ModalManager, PointerManager, Capabilities,
                  SearchCtrlHelper) {

  var rootCtrl = this;

  /**
   * Stores the serialized version of the queue. Useful to check the new url state against the internal state
   * to prevent infinite loops when changing the url internally.
   *
   * @type {string}
   */
  var serializedQueue;

  rootCtrl.queueLoading = false;

  rootCtrl.isSearchShown = isSearchShown;
  rootCtrl.getQueue = getQueue;
  rootCtrl.getRunningQueueEntry = getRunningQueueEntry;
  rootCtrl.getLoadingQueueEntry = getLoadingQueueEntry;
  rootCtrl.isPlaying = isPlaying;
  rootCtrl.shouldIdleChrome = shouldIdleChrome;
  rootCtrl.isMouseDetected = isMouseDetected;
  rootCtrl.shouldShowScene = shouldShowScene;
  rootCtrl.shouldShowPlaybackControls = shouldShowPlaybackControls;
  rootCtrl.toggleSearch = SearchCtrlHelper.toggleSearch;
  rootCtrl.togglePlayback = togglePlayback;

  // setup direct access to the property for double binding
  Object.defineProperty(rootCtrl, 'searchTerm', {
    get: function() {
      return SearchCtrlHelper.searchTerm;
    },
    set: function(value) {
      SearchCtrlHelper.searchTerm = value;
    }
  });

  function isSearchShown() {
    return SearchCtrlHelper.searchShown;
  }

  function getQueue() {
    return QueueManager.queue;
  }

  function getRunningQueueEntry() {
    return Orchestrator.runningQueueEntry;
  }

  function getLoadingQueueEntry() {
    return Orchestrator.loadingQueueEntry;
  }

  function isPlaying() {
    return Orchestrator.playing;
  }

  function shouldIdleChrome() {
    return !UserInteractionManager.userInteracting;
  }

  function isMouseDetected() {
    return PointerManager.mouseDetected;
  }

  function shouldShowScene() {
    return Capabilities.playback;
  }

  function shouldShowPlaybackControls() {
    return Capabilities.playback || Capabilities.remoteControl;
  }

  function togglePlayback() {
    Orchestrator.togglePlayback();
  }

  // we need to wait for the loading phase to be done to avoid race problems (loading for ever)
  $timeout(activate);

  function activate() {
    // hide the input search at startup
    SearchCtrlHelper.toggleSearch(false);

    // register the global space shortcut
    KeyboardShortcutManager.register('space', function(evt) {
      evt.preventDefault();
      Orchestrator.togglePlayback();
    });

    KeyboardShortcutManager.register('search', 'esc', function(evt) {
      evt.preventDefault();
      SearchCtrlHelper.toggleSearch(false);
    });

    // prevents the backspace shortcut
    // it is really easy to inadvertently hit the key and  triggers a "Go Back" action
    KeyboardShortcutManager.register('backspace', function(evt) {
      evt.preventDefault();
    });

    $scope.$watch(function() {
      return QueueManager.queue;
    }, function(newVal, oldVal) {
      // this test is here to prevent to serialize during the init phase
      if (newVal !== oldVal) {
        var newSerializedQueue = QueueManager.serialize();
        if (serializedQueue !== newSerializedQueue) {
          serializedQueue = newSerializedQueue;
          // replace queue parameter but keep the rest
          $location.search(angular.extend({}, $location.search(), {queue: serializedQueue}));
        }
      }
    }, true);

    $scope.$watch(function() {
      return $location.search().queue;
    }, function(newSerializedQueue) {
      if (serializedQueue !== newSerializedQueue) {
        serializedQueue = newSerializedQueue;
        // change initiated by user (back / forward etc.), need to be deserialized
        rootCtrl.queueLoading = true;
        QueueManager.deserialize(serializedQueue).catch(function(message) {
          NotificationCentersRegistry('notificationCenter').ready(function(notificationCenter) {
            notificationCenter.error(message);
          });
        }).finally(function() {
          rootCtrl.queueLoading = false;
        });
      }
    });

    $scope.$watch(function() {
      return Orchestrator.runningQueueEntry;
    }, function(runningQueueEntry, oldVal) {
      if (runningQueueEntry !== oldVal) {
        QueuesRegistry('queue').ready(function(queue) {
          queue.focusEntry(runningQueueEntry);
        });
      }
    });

    $scope.$watch(function() {
      return Capabilities.playback;
    }, function(playback) {
      if (playback === false) {
        ModalManager.open({
          title: 'MixTube won\'t work on your device',
          contentTemplateUrl: '/scripts/components/capabilities/no-playback-modal-content.html',
          commands: [{label: 'OK', primary: true}]
        });
      }
    });
  }
}

function SearchResultsCtrl($scope, $timeout, YoutubeClient, SearchCtrlHelper) {

  var searchResultsCtrl = this;

  /**
   * @const
   * @type {number}
   */
  var INSTANT_SEARCH_DELAY = 500;

  /** @type {number} */
  var searchRequestCount = 0;
  /** @type {?Promise} */
  var instantSearchPromise = null;

  // the following variables will be initialized by the initSearch function.

  /**
   * The user already executed one search. Used to hide the results pane until there is something to show.
   *
   * @type {boolean}
   */
  searchResultsCtrl.inSearch = null;
  /**
   * A list of results pages.
   *
   * @type {Object.<string, Array.<Array.<Video>>>}
   */
  searchResultsCtrl.results = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.pending = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.pendingMore = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.error = null;
  /** @type {Object.<string, boolean>} */
  searchResultsCtrl.noneFound = null;
  /** @type {Object.<string, string>} */
  searchResultsCtrl.nextPageId = null;

  searchResultsCtrl.getSearchTerm = getSearchTerm;
  searchResultsCtrl.shouldShowSearchResultPanel = shouldShowSearchResultPanel;
  searchResultsCtrl.showMore = showMore;

  activate();

  function getSearchTerm() {
    return SearchCtrlHelper.searchTerm;
  }

  function shouldShowSearchResultPanel() {
    return SearchCtrlHelper.searchShown && searchResultsCtrl.inSearch;
  }

  function initSearch() {
    instantSearchPromise = null;
    searchResultsCtrl.inSearch = false;
    searchResultsCtrl.results = {youtube: [[]]};
    searchResultsCtrl.pending = {youtube: false};
    searchResultsCtrl.pendingMore = {youtube: false};
    searchResultsCtrl.nextPageId = {youtube: null};
    searchResultsCtrl.error = {youtube: false};
    searchResultsCtrl.noneFound = {youtube: false};
  }

  function showMore(pId, nextPageId) {
    if (pId === 'youtube') {
      // clear any error message (case of retry after error)
      searchResultsCtrl.error.youtube = false;
      searchYoutube(SearchCtrlHelper.searchTerm, nextPageId);
    }
  }

  /**
   * @param {string} term
   * @param {string=} nextPageId
   * @returns {Promise}
   */
  function searchYoutube(term, nextPageId) {
    var first = !nextPageId;

    var startSearchRequestCount = searchRequestCount;

    if (first) {
      searchResultsCtrl.pending.youtube = true;

      // reset the results list and the next page token since we are starting a new search
      searchResultsCtrl.results.youtube = [];
      searchResultsCtrl.nextPageId.youtube = null;
    } else {
      searchResultsCtrl.pendingMore.youtube = true;
    }

    return YoutubeClient.searchVideosByQuery(term,
      {pageSize: first ? 11 : 12, pageId: nextPageId}).then(function doneCb() {
        if (searchRequestCount === startSearchRequestCount) {
          if (first) {
            searchResultsCtrl.pending.youtube = false;
          } else {
            searchResultsCtrl.pendingMore.youtube = false;
          }
        }
      }, null, function progressCb(results) {
        if (searchRequestCount === startSearchRequestCount) {
          if (results.videos.length) {
            searchResultsCtrl.results.youtube.push(results.videos);
            searchResultsCtrl.nextPageId.youtube = results.nextPageId;
          } else {
            searchResultsCtrl.noneFound.youtube = true;
          }
        }
      }).catch(function catchCb() {
        if (searchRequestCount === startSearchRequestCount) {
          searchResultsCtrl.error.youtube = true;
          if (first) {
            searchResultsCtrl.pending.youtube = false;
            searchResultsCtrl.results.youtube = [];
          } else {
            searchResultsCtrl.pendingMore.youtube = false;
          }
        }
      });
  }

  function activate() {
    initSearch();

    // when the user types we automatically execute the search
    $scope.$watch(function() {
      return SearchCtrlHelper.searchTerm;
    }, function(newSearchTerm) {
      if (newSearchTerm !== null) {

        // new inputs so we stop the previous request
        $timeout.cancel(instantSearchPromise);

        // as soon as the query changes clear messages
        searchResultsCtrl.error.youtube = false;
        searchResultsCtrl.noneFound.youtube = false;

        // if the search has to be longer than two characters
        if (newSearchTerm.length > 2) {
          searchRequestCount++;

          $timeout.cancel(instantSearchPromise);
          instantSearchPromise = $timeout(function search() {
            searchResultsCtrl.inSearch = true;
            searchYoutube(newSearchTerm);
          }, INSTANT_SEARCH_DELAY);
        }
      }
    });

    // ensures everything is initialized when the search is shown and stopped when it is hidden
    $scope.$watch(function() {
      return SearchCtrlHelper.searchShown;
    }, function(searchShown) {
      if (searchShown) {
        initSearch();
      } else {
        $timeout.cancel(instantSearchPromise);
      }
    });
  }
}

function SearchResultCtrl($timeout, QueueManager, QueuesRegistry, Orchestrator) {

  var searchResultCtrl = this;

  /**
   * @const
   * @type {number}
   */
  var CONFIRMATION_DURATION = 4000;

  /** @type {?Promise} */
  var tmoPromise = null;

  /** @type {boolean} */
  searchResultCtrl.shouldShowConfirmation = false;
  /** @type {?number} */
  searchResultCtrl.countBeforePlayback = null;

  searchResultCtrl.appendResultToQueue = appendResultToQueue;

  /**
   * @param {mt.Video} video
   */
  function appendResultToQueue(video) {

    var queueEntry = QueueManager.appendVideo(video);

    if (Orchestrator.runningQueueEntry) {
      var entries = QueueManager.queue.entries;
      searchResultCtrl.countBeforePlayback = entries.indexOf(queueEntry) - entries.indexOf(Orchestrator.runningQueueEntry);
    } else {
      searchResultCtrl.countBeforePlayback = null;
    }

    QueuesRegistry('queue').ready(function(queue) {
      queue.focusEntry(queueEntry);
    });

    searchResultCtrl.shouldShowConfirmation = true;
    $timeout.cancel(tmoPromise);
    tmoPromise = $timeout(function() {
      searchResultCtrl.shouldShowConfirmation = false;
    }, CONFIRMATION_DURATION);
  }
}

function QueueCtrl(Orchestrator, QueueManager) {

  var queueCtrl = this;

  queueCtrl.playQueueEntry = playQueueEntry;
  queueCtrl.removeQueueEntry = removeQueueEntry;

  /**
   * @param {number} queueIndex
   */
  function playQueueEntry(queueIndex) {
    Orchestrator.skipTo(queueIndex);
  }

  /**
   * @param {mt.QueueEntry} queueEntry
   */
  function removeQueueEntry(queueEntry) {
    QueueManager.removeEntry(queueEntry);
  }
}

function DebuggingCtrl(Configuration, KeyboardShortcutManager, NotificationCentersRegistry) {

  activate();

  function notification(message) {
    NotificationCentersRegistry('notificationCenter').ready(function(notificationCenter) {
      notificationCenter.error(message);
    });
  }

  function activate() {
    if (Configuration.debugNotifications) {
      // register the global space shortcut
      KeyboardShortcutManager.register('ctrl+n', function(evt) {
        evt.preventDefault();
        notification('Debugging: Test notification');
      });
    }
  }
}

exports.RootCtrl = RootCtrl;
exports.SearchResultsCtrl = SearchResultsCtrl;
exports.SearchResultCtrl = SearchResultCtrl;
exports.QueueCtrl = QueueCtrl;
exports.DebuggingCtrl = DebuggingCtrl;<|endoftext|>


<commit_before>(function() {
  "use strict";

  // See https://angular-translate.github.io/docs/#/guide
  angular.module('dimApp')
    .config(['$translateProvider', function($translateProvider) {
      $translateProvider.useSanitizeValueStrategy('escape');

      $translateProvider
        .translations('en', {
          Level: "Level",
          Weapons: "Weapons",
          Armor: "Armor",
          General: "General",
          Postmaster: "Postmaster",
          Equip: "Equip",
          Store: "Store",
          Vault: "Vault",
          Vanguard: "Vanguard",
          Reputation: "Reputation",
          Intellect: "Intellect",
          Discipline: "Discipline",
          Strength: "Strength",
          Super_cooldown: "Super cooldown",
          Grenade_cooldown: "Grenade cooldown",
          Melee_cooldown: "Melee cooldown",
          tier_progress: "{{progress}} for {{tier}}",
          About: "About",
          'Support DIM': "Support DIM",
          Vendors: "Vendors",
          Support: "Support",
          Settings: "Settings",
          filter_help: "Search item/perk or is:arc",
          'Tag Item': "Tag Item",
          Favorite: "Favorite",
          Keep: "Keep",
          Junk: "Junk",
          Infuse: "Infuse",
          notes_placeholder: "Add notes to this item",
          before_loadout: "Before '{{name}}'",
          'Create Loadout': "Create Loadout",
          loadout_from_equipped: "Equipped",
          'Edit Loadout': "Edit Loadout",
          'Delete Loadout': "Delete Loadout",
          apply_search_loadout: "Items = \"{{query}}\"",
          'Maximize Light': "Maximize Light",
          'Item Leveling': "Item Leveling",
          'Gather Engrams': "Gather Engrams",
          gather_engrams_except_exotics: "Exotics",
          farming_mode: "Farming Mode (move items)",
          restore_all_items: "All Items",
          Loadouts: "Loadouts",
          LoadoutAppliedAuto: "Automatic Loadout Builder",
          LoadoutApplied: "Your loadout of {{amount}} items have been transferred to your {{store}}.",
          LoadoutAppliedError: "None of the items in your loadout could be transferred.",
          LoadoutAppliedWarn: "Your loadout has been partially transferred, but {{failed}} of {{total}} items had errors."
        })
        .translations('it', {
          Level: "Livello",
          Weapons: "Armi",
          Armor: "Armatura",
          General: "Generale",
          Postmaster: "Amministratrice",
          Equip: "Equipaggia",
          Store: "Memorizzare",
          Vault: "Depositi",
          Vanguard: "Avanguardia",
          Reputation: "Reputazione",
          Intellect: "Intelletto",
          Discipline: "Disciplina",
          Strength: "Forza",
          Super_cooldown: "Super tempo di recupero",
          Grenade_cooldown: "Granate tempo di recupero",
          Melee_cooldown: "Corpo a corpo tempo di recupero",
          tier_progress: "{{progress}} per {{tier}}",
          About: "Chi siamo",
          'Support DIM': "Aiutare DIM",
          Vendors: "Mercanti",
          Support: "Aiutare",
          Settings: "Impostazioni",
          'Tag Item': "Elemento Tag",
          Favorite: "Preferito",
          Keep: "Tenere",
          Junk: "Giunca",
          Infuse: "Infondi",
          notes_placeholder: "Aggiungere note a questa voce",
          'Create Loadout': "Creare Loadout",
          loadout_from_equipped: "Fornito",
          'Edit Loadout': "Modifica Loadout",
          'Delete Loadout': "Cancellare Loadout",
          apply_search_loadout: "Elementi = \"{{query}}\"",
          'Maximize Light': "Massimizzare la Luce",
          'Item Leveling': "Elemento di Livellamento",
          'Gather Engrams': "Raccogliere Engram",
          gather_engrams_except_exotics: "Esotiche",
          farming_mode: "Engram a Depositi (Coltura)",
          restore_all_items: "Tutti gli Elementi",
          Loadouts: "Loadouts"
        })
        .translations('de', {
          Weapons: "Waffen",
          Armor: "Rüstung",
          General: "Allgemein",
          Postmaster: "Poststelle",
          Equip: "Ausrüsten",
          Store: "Verstauen",
          Vault: "Tresor",
          Vanguard: "Vorhut",
          Reputation: "Ruf",
          Intellect: "Intellekt",
          Discipline: "Disziplin",
          Strength: "Stärke",
          Super_cooldown: "Super Abklingzeit",
          Grenade_cooldown: "Granaten Abklingzeit",
          Melee_cooldown: "Nahkampf Abklingzeit",
          tier_progress: "{{progress}} für {{tier}}",
          About: "Über",
          'Support DIM': "DIM Unterstützen",
          Vendors: "Händler",
          Support: "Unterstützen",
          Settings: "Einstellungen",
          'Tag Item': "Tag Artikel",
          Favorite: "Favorit",
          Keep: "Behalten",
          Junk: "Trödel",
          Infuse: "Infundieren",
          notes_placeholder: "Hinzufügen von Anmerkungen zu diesem Artikel",
          'Create Loadout': "Loadout erstellen",
          loadout_from_equipped: "Aus angelegter Ausrüstung",
          'Edit Loadout': "Loadout bearbeiten",
          'Delete Loadout': "Loadout löschen",
          apply_search_loadout: "Gegenstand = \"{{query}}\"",
          'Maximize Light': "Licht maximieren",
          'Item Leveling': "Gegenstand aufwerten",
          'Gather Engrams': "Engramme sammeln",
          gather_engrams_except_exotics: "Exotics",
          farming_mode: "Engramme zum Tresor",
          restore_all_items: "Alle Elemente",
          Loadouts: "Loadouts"
        })
        .translations('fr', {
          Level: "Niveau",
          Weapons: "Armes",
          Armor: "Armure",
          General: "Général",
          Postmaster: "Commis des postes",
          Equip: "Équiper",
          Store: "Stocker",
          Vault: "Coffres",
          Vanguard: "Avant-garde",
          Reputation: "Estime",
          Intellect: "Intelligence",
          Discipline: "Discipline",
          Strength: "Force",
          Super_cooldown: "Super délai",
          Grenade_cooldown: "Grenade délai",
          Melee_cooldown: "Mêlée délai",
          tier_progress: "{{progress}} pour {{tier}}",
          About: "À propos",
          'Support DIM': "Soutien DIM",
          Vendors: "Marchands",
          Support: "Soutien",
          Settings: "Paramètres",
          'Tag Item': "Tag Article",
          Favorite: "Préféré",
          Keep: "Garder",
          Junk: "Jonque",
          Infuse: "Infuser",
          notes_placeholder: "Ajouter des notes à cet article",
          'Create Loadout': "Créer Loadout",
          loadout_from_equipped: "Equipée",
          'Edit Loadout': "Modifier Loadout",
          'Delete Loadout': "Effacer Loadout",
          apply_search_loadout: "Articles = \"{{query}}\"",
          'Maximize Light': "Maximiser la Lumière",
          'Item Leveling': "Article Nivellement",
          'Gather Engrams': "Recueillir des Engrammes",
          gather_engrams_except_exotics: "Exotiques",
          farming_mode: "Engrammes Coffres (Agriculture)",
          restore_all_items: "Tous les Articles",
          Loadouts: "Loadouts"
        })
        .translations('es', {
          Level: "Nivel",
          Weapons: "Armas",
          Armor: "Armadura",
          Postmaster: "Administración",
          Equip: "Equipar",
          Store: "Almacenar",
          Vault: "Depósito",
          Vanguard: "Vanguardia",
          Reputation: "Reputación",
          Intellect: "Intelecto",
          Discipline: "Disciplina",
          Strength: "Fuerza",
          Super_cooldown: "Super tiempo",
          Grenade_cooldown: "Granade tiempo",
          Melee_cooldown: "Cuerpo a cuerpo tiempo",
          tier_progress: "{{progress}} por {{tier}}",
          About: "Sobre",
          'Support DIM': "Apoyo DIM",
          Vendors: "Comerciantes",
          Support: "Apoyo",
          Settings: "Configuración",
          'Tag Item': "Elemento de Etiqueta",
          Favorite: "Favorito",
          Keep: "Guardar",
          Junk: "Basura",
          Infuse: "Infundir",
          notes_placeholder: "Agregar notas a este artículo",
          'Create Loadout': "Crear Loadout",
          loadout_from_equipped: "Equipado",
          'Edit Loadout': "Editar Loadout",
          'Delete Loadout': "Borrar Loadout",
          apply_search_loadout: "Artículos = \"{{query}}\"",
          'Maximize Light': "Maximizar la Luz",
          'Item Leveling': "Nivelación Elemento",
          'Gather Engrams': "Recopilar Engramas",
          gather_engrams_except_exotics: "Exóticas",
          farming_mode: "Engramas a Depósito (Agricultura)",
          restore_all_items: "Todos los Artículos",
          Loadouts: "Loadouts"
        })
        .translations('ja', {
          Level: "レベル",
          Weapons: "武器",
          Armor: "よろい",
          General: "全般",
          Postmaster: "ポストマスター",
          Equip: "装備する",
          Vault: "装備",
          Vanguard: "バンガード",
          Reputation: "評価",
          Intellect: "知性",
          Discipline: "鍛錬",
          Strength: "腕力",
          About: "紹介",
          'Support DIM': "サポート DIM",
          Vendors: "ベンダー",
          Support: "サポート",
          Settings: "設定",
          'Tag Item': "タグアイテム",
          Favorite: "本命",
          Keep: "保つ",
          Junk: "ジャンク",
          Infuse: "煎じる",
          notes_placeholder: "このアイテムにメモを追加",
          'Create Loadout': "作る Loadout",
          loadout_from_equipped: "備える",
          'Edit Loadout': "編集 Loadout",
          'Delete Loadout': "削除 Loadout",
          apply_search_loadout: "箇条 = \"{{query}}\"",
          'Maximize Light': "ライトを最大化",
          'Item Leveling': "アイテムの平準化",
          'Gather Engrams': "エングラムを収集",
          gather_engrams_except_exotics: "エキゾチック",
          farming_mode: "エングラムは、 装備 (農業)",
          restore_all_items: "品揃え",
          Loadouts: "Loadouts"
        })
        .translations('pt-br', {
          Level: "Nível",
          Weapons: "Armas",
          Armor: "Armadura",
          General: "Geral",
          Postmaster: "Chefe do Correio",
          Equip: "Equipar",
          Store: "Armazenar",
          Vault: "Cofres",
          Vanguard: "Vanguarda",
          Reputation: "Reputação",
          Intellect: "Intelecto",
          Discipline: "Disciplina",
          Strength: "Força",
          Super_cooldown: "Super tempo",
          Grenade_cooldown: "Granadas tempo",
          Melee_cooldown: "Corpo a corpo tempo",
          tier_progress: "{{progress}} por {{tier}}",
          About: "Sobre",
          'Support DIM': "Apoio DIM",
          Vendors: "Vendedores",
          Support: "Apoio",
          Settings: "Configurações",
          'Tag Item': "Item de Tag",
          Favorite: "Favorito",
          Keep: "Guarda",
          Junk: "Lixo",
          Infuse: "Infundir",
          notes_placeholder: "Adicionar observações a este item",
          'Create Loadout': "Crio Loadout",
          loadout_from_equipped: "Equipado",
          'Edit Loadout': "Editar Loadout",
          'Delete Loadout': "Excluir Loadout",
          apply_search_loadout: "Itens = \"{{query}}\"",
          'Maximize Light': "Maximizar a Luz",
          'Item Leveling': "Nivelamento Item",
          'Gather Engrams': "Reunir Engramas",
          gather_engrams_except_exotics: "Exóticos",
          farming_mode: "Engramas a Cofres (Agricultura)",
          restore_all_items: "Todos os Itens",
          Loadouts: "Loadouts"
        })
        .fallbackLanguage('en');
    }]);
})();
<commit_msg>Translate 'Progress'<commit_after>(function() {
  "use strict";

  // See https://angular-translate.github.io/docs/#/guide
  angular.module('dimApp')
    .config(['$translateProvider', function($translateProvider) {
      $translateProvider.useSanitizeValueStrategy('escape');

      $translateProvider
        .translations('en', {
          Level: "Level",
          Weapons: "Weapons",
          Armor: "Armor",
          General: "General",
          Postmaster: "Postmaster",
          Equip: "Equip",
          Store: "Store",
          Vault: "Vault",
          Vanguard: "Vanguard",
          Reputation: "Reputation",
          Intellect: "Intellect",
          Discipline: "Discipline",
          Strength: "Strength",
          Super_cooldown: "Super cooldown",
          Grenade_cooldown: "Grenade cooldown",
          Melee_cooldown: "Melee cooldown",
          tier_progress: "{{progress}} for {{tier}}",
          About: "About",
          'Support DIM': "Support DIM",
          Vendors: "Vendors",
          Support: "Support",
          Settings: "Settings",
          filter_help: "Search item/perk or is:arc",
          'Tag Item': "Tag Item",
          Favorite: "Favorite",
          Keep: "Keep",
          Junk: "Junk",
          Infuse: "Infuse",
          notes_placeholder: "Add notes to this item",
          before_loadout: "Before '{{name}}'",
          'Create Loadout': "Create Loadout",
          loadout_from_equipped: "Equipped",
          'Edit Loadout': "Edit Loadout",
          'Delete Loadout': "Delete Loadout",
          apply_search_loadout: "Items = \"{{query}}\"",
          'Maximize Light': "Maximize Light",
          'Item Leveling': "Item Leveling",
          'Gather Engrams': "Gather Engrams",
          gather_engrams_except_exotics: "Exotics",
          farming_mode: "Farming Mode (move items)",
          restore_all_items: "All Items",
          Loadouts: "Loadouts",
          Progress: "Progess",
          LoadoutAppliedAuto: "Automatic Loadout Builder",
          LoadoutApplied: "Your loadout of {{amount}} items have been transferred to your {{store}}.",
          LoadoutAppliedError: "None of the items in your loadout could be transferred.",
          LoadoutAppliedWarn: "Your loadout has been partially transferred, but {{failed}} of {{total}} items had errors."
        })
        .translations('it', {
          Level: "Livello",
          Weapons: "Armi",
          Armor: "Armatura",
          General: "Generale",
          Postmaster: "Amministratrice",
          Equip: "Equipaggia",
          Store: "Memorizzare",
          Vault: "Depositi",
          Vanguard: "Avanguardia",
          Reputation: "Reputazione",
          Intellect: "Intelletto",
          Discipline: "Disciplina",
          Strength: "Forza",
          Super_cooldown: "Super tempo di recupero",
          Grenade_cooldown: "Granate tempo di recupero",
          Melee_cooldown: "Corpo a corpo tempo di recupero",
          tier_progress: "{{progress}} per {{tier}}",
          About: "Chi siamo",
          'Support DIM': "Aiutare DIM",
          Vendors: "Mercanti",
          Support: "Aiutare",
          Settings: "Impostazioni",
          'Tag Item': "Elemento Tag",
          Favorite: "Preferito",
          Keep: "Tenere",
          Junk: "Giunca",
          Infuse: "Infondi",
          notes_placeholder: "Aggiungere note a questa voce",
          'Create Loadout': "Creare Loadout",
          loadout_from_equipped: "Fornito",
          'Edit Loadout': "Modifica Loadout",
          'Delete Loadout': "Cancellare Loadout",
          apply_search_loadout: "Elementi = \"{{query}}\"",
          'Maximize Light': "Massimizzare la Luce",
          'Item Leveling': "Elemento di Livellamento",
          'Gather Engrams': "Raccogliere Engram",
          gather_engrams_except_exotics: "Esotiche",
          farming_mode: "Engram a Depositi (Coltura)",
          restore_all_items: "Tutti gli Elementi",
          Loadouts: "Loadouts",
          Progress: "Progesso"
        })
        .translations('de', {
          Weapons: "Waffen",
          Armor: "Rüstung",
          General: "Allgemein",
          Postmaster: "Poststelle",
          Equip: "Ausrüsten",
          Store: "Verstauen",
          Vault: "Tresor",
          Vanguard: "Vorhut",
          Reputation: "Ruf",
          Intellect: "Intellekt",
          Discipline: "Disziplin",
          Strength: "Stärke",
          Super_cooldown: "Super Abklingzeit",
          Grenade_cooldown: "Granaten Abklingzeit",
          Melee_cooldown: "Nahkampf Abklingzeit",
          tier_progress: "{{progress}} für {{tier}}",
          About: "Über",
          'Support DIM': "DIM Unterstützen",
          Vendors: "Händler",
          Support: "Unterstützen",
          Settings: "Einstellungen",
          'Tag Item': "Tag Artikel",
          Favorite: "Favorit",
          Keep: "Behalten",
          Junk: "Trödel",
          Infuse: "Infundieren",
          notes_placeholder: "Hinzufügen von Anmerkungen zu diesem Artikel",
          'Create Loadout': "Loadout erstellen",
          loadout_from_equipped: "Aus angelegter Ausrüstung",
          'Edit Loadout': "Loadout bearbeiten",
          'Delete Loadout': "Loadout löschen",
          apply_search_loadout: "Gegenstand = \"{{query}}\"",
          'Maximize Light': "Licht maximieren",
          'Item Leveling': "Gegenstand aufwerten",
          'Gather Engrams': "Engramme sammeln",
          gather_engrams_except_exotics: "Exotics",
          farming_mode: "Engramme zum Tresor",
          restore_all_items: "Alle Elemente",
          Loadouts: "Loadouts",
          Progress: "Fortschritt"
        })
        .translations('fr', {
          Level: "Niveau",
          Weapons: "Armes",
          Armor: "Armure",
          General: "Général",
          Postmaster: "Commis des postes",
          Equip: "Équiper",
          Store: "Stocker",
          Vault: "Coffres",
          Vanguard: "Avant-garde",
          Reputation: "Estime",
          Intellect: "Intelligence",
          Discipline: "Discipline",
          Strength: "Force",
          Super_cooldown: "Super délai",
          Grenade_cooldown: "Grenade délai",
          Melee_cooldown: "Mêlée délai",
          tier_progress: "{{progress}} pour {{tier}}",
          About: "À propos",
          'Support DIM': "Soutien DIM",
          Vendors: "Marchands",
          Support: "Soutien",
          Settings: "Paramètres",
          'Tag Item': "Tag Article",
          Favorite: "Préféré",
          Keep: "Garder",
          Junk: "Jonque",
          Infuse: "Infuser",
          notes_placeholder: "Ajouter des notes à cet article",
          'Create Loadout': "Créer Loadout",
          loadout_from_equipped: "Equipée",
          'Edit Loadout': "Modifier Loadout",
          'Delete Loadout': "Effacer Loadout",
          apply_search_loadout: "Articles = \"{{query}}\"",
          'Maximize Light': "Maximiser la Lumière",
          'Item Leveling': "Article Nivellement",
          'Gather Engrams': "Recueillir des Engrammes",
          gather_engrams_except_exotics: "Exotiques",
          farming_mode: "Engrammes Coffres (Agriculture)",
          restore_all_items: "Tous les Articles",
          Loadouts: "Loadouts",
          Progress: "La Progression"
        })
        .translations('es', {
          Level: "Nivel",
          Weapons: "Armas",
          Armor: "Armadura",
          Postmaster: "Administración",
          Equip: "Equipar",
          Store: "Almacenar",
          Vault: "Depósito",
          Vanguard: "Vanguardia",
          Reputation: "Reputación",
          Intellect: "Intelecto",
          Discipline: "Disciplina",
          Strength: "Fuerza",
          Super_cooldown: "Super tiempo",
          Grenade_cooldown: "Granade tiempo",
          Melee_cooldown: "Cuerpo a cuerpo tiempo",
          tier_progress: "{{progress}} por {{tier}}",
          About: "Sobre",
          'Support DIM': "Apoyo DIM",
          Vendors: "Comerciantes",
          Support: "Apoyo",
          Settings: "Configuración",
          'Tag Item': "Elemento de Etiqueta",
          Favorite: "Favorito",
          Keep: "Guardar",
          Junk: "Basura",
          Infuse: "Infundir",
          notes_placeholder: "Agregar notas a este artículo",
          'Create Loadout': "Crear Loadout",
          loadout_from_equipped: "Equipado",
          'Edit Loadout': "Editar Loadout",
          'Delete Loadout': "Borrar Loadout",
          apply_search_loadout: "Artículos = \"{{query}}\"",
          'Maximize Light': "Maximizar la Luz",
          'Item Leveling': "Nivelación Elemento",
          'Gather Engrams': "Recopilar Engramas",
          gather_engrams_except_exotics: "Exóticas",
          farming_mode: "Engramas a Depósito (Agricultura)",
          restore_all_items: "Todos los Artículos",
          Loadouts: "Loadouts",
          Progress: "Progreso"
        })
        .translations('ja', {
          Level: "レベル",
          Weapons: "武器",
          Armor: "よろい",
          General: "全般",
          Postmaster: "ポストマスター",
          Equip: "装備する",
          Vault: "装備",
          Vanguard: "バンガード",
          Reputation: "評価",
          Intellect: "知性",
          Discipline: "鍛錬",
          Strength: "腕力",
          About: "紹介",
          'Support DIM': "サポート DIM",
          Vendors: "ベンダー",
          Support: "サポート",
          Settings: "設定",
          'Tag Item': "タグアイテム",
          Favorite: "本命",
          Keep: "保つ",
          Junk: "ジャンク",
          Infuse: "煎じる",
          notes_placeholder: "このアイテムにメモを追加",
          'Create Loadout': "作る Loadout",
          loadout_from_equipped: "備える",
          'Edit Loadout': "編集 Loadout",
          'Delete Loadout': "削除 Loadout",
          apply_search_loadout: "箇条 = \"{{query}}\"",
          'Maximize Light': "ライトを最大化",
          'Item Leveling': "アイテムの平準化",
          'Gather Engrams': "エングラムを収集",
          gather_engrams_except_exotics: "エキゾチック",
          farming_mode: "エングラムは、 装備 (農業)",
          restore_all_items: "品揃え",
          Loadouts: "Loadouts",
          Progress: "進捗"
        })
        .translations('pt-br', {
          Level: "Nível",
          Weapons: "Armas",
          Armor: "Armadura",
          General: "Geral",
          Postmaster: "Chefe do Correio",
          Equip: "Equipar",
          Store: "Armazenar",
          Vault: "Cofres",
          Vanguard: "Vanguarda",
          Reputation: "Reputação",
          Intellect: "Intelecto",
          Discipline: "Disciplina",
          Strength: "Força",
          Super_cooldown: "Super tempo",
          Grenade_cooldown: "Granadas tempo",
          Melee_cooldown: "Corpo a corpo tempo",
          tier_progress: "{{progress}} por {{tier}}",
          About: "Sobre",
          'Support DIM': "Apoio DIM",
          Vendors: "Vendedores",
          Support: "Apoio",
          Settings: "Configurações",
          'Tag Item': "Item de Tag",
          Favorite: "Favorito",
          Keep: "Guarda",
          Junk: "Lixo",
          Infuse: "Infundir",
          notes_placeholder: "Adicionar observações a este item",
          'Create Loadout': "Crio Loadout",
          loadout_from_equipped: "Equipado",
          'Edit Loadout': "Editar Loadout",
          'Delete Loadout': "Excluir Loadout",
          apply_search_loadout: "Itens = \"{{query}}\"",
          'Maximize Light': "Maximizar a Luz",
          'Item Leveling': "Nivelamento Item",
          'Gather Engrams': "Reunir Engramas",
          gather_engrams_except_exotics: "Exóticos",
          farming_mode: "Engramas a Cofres (Agricultura)",
          restore_all_items: "Todos os Itens",
          Loadouts: "Loadouts",
          Progress: "Progresso"
        })
        .fallbackLanguage('en');
    }]);
})();
<|endoftext|>