<commit_before>// Copyright 2010 WebDriver committers
// Copyright 2010 Google Inc.
//
// 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.

/**
 * @fileoverview Atoms-based implementation of the webelement interface.
 */

goog.provide('webdriver.atoms.element');

goog.require('bot.Keyboard.Keys');
goog.require('bot.action');
goog.require('bot.dom');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.math');
goog.require('goog.string');
goog.require('goog.style');
goog.require('webdriver.Key');

/**
 * @param {!Element} element The element to use.
 * @return {boolean} Whether the element is checked or selected.
 */
webdriver.atoms.element.isSelected = function(element) {
  // Although this method looks unloved, its compiled form is used by
  // the Chrome and OperaDrivers.
  if (!bot.dom.isSelectable(element)) {
    return false;
  }

  return bot.dom.isSelected(element);
};

/**
 * Common aliases for properties. This maps names that users use to the correct
 * property name.
 *
 * @const
 * @private
 */
webdriver.atoms.element.PROPERTY_ALIASES_ = {
  'class': 'className',
  'readonly': 'readOnly'
};

/**
 * Used to determine whether we should return a boolean value from getAttribute.
 * These are all extracted from the WHATWG spec:
 *
 *   http://www.whatwg.org/specs/web-apps/current-work/
 *
 * These must all be lower-case.
 *
 * @const
 * @private
 */
webdriver.atoms.element.BOOLEAN_PROPERTIES_ = [
  'async',
  'autofocus',
  'autoplay',
  'checked',
  'compact',
  'complete',
  'controls',
  'declare',
  'defaultchecked',
  'defaultselected',
  'defer',
  'disabled',
  'draggable',
  'ended',
  'formnovalidate',
  'hidden',
  'indeterminate',
  'iscontenteditable',
  'ismap',
  'itemscope',
  'loop',
  'multiple',
  'muted',
  'nohref',
  'noresize',
  'noshade',
  'novalidate',
  'nowrap',
  'open',
  'paused',
  'pubdate',
  'readonly',
  'required',
  'reversed',
  'scoped',
  'seamless',
  'seeking',
  'selected',
  'spellcheck',
  'truespeed',
  'willvalidate'
];

/**
 * Get the value of the given property or attribute. If the "attribute" is for
 * a boolean property, we return null in the case where the value is false. If
 * the attribute name is "style" an attempt to convert that style into a string
 * is done.
 *
 * @param {!Element} element The element to use.
 * @param {string} attribute The name of the attribute to look up.
 * @return {?string} The string value of the attribute or property, or null.
 */
webdriver.atoms.element.getAttribute = function(element, attribute) {
  var value = null;
  var name = attribute.toLowerCase();

  if ('style' == attribute.toLowerCase()) {
    value = element.style;

    if (value && !goog.isString(value)) {
      value = value.cssText;
    }

    return (/** @type {?string} */value);
  }

  if (('selected' == name || 'checked' == name) &&
      bot.dom.isSelectable(element)) {
    return bot.dom.isSelected(element) ? 'true' : null;
  }

  // Our tests suggest that returning the attribute is desirable for
  // the href attribute of <a> tags and the src attribute of <img> tags,
  // but we normally attempt to get the property value before the attribute.
  var isLink = bot.dom.isElement(element, goog.dom.TagName.A);
  var isImg = bot.dom.isElement(element, goog.dom.TagName.IMG);

  // Although the attribute matters, the property is consistent. Return that in
  // preference to the attribute for links and images.
  if ((isImg && name == 'src') || (isLink && name == 'href')) {
    value = bot.dom.getAttribute(element, name);
    if (value) {
      // We want the full URL if present
      value = bot.dom.getProperty(element, name);
    }
    return (/** @type {?string} */value);
  }

  var propName = webdriver.atoms.element.PROPERTY_ALIASES_[attribute] ||
      attribute;
  if (goog.array.contains(webdriver.atoms.element.BOOLEAN_PROPERTIES_, name)) {
    value = goog.isDefAndNotNull(bot.dom.getAttribute(element, attribute)) ||
        bot.dom.getProperty(element, propName);
    return value ? 'true' : null;
  }

  var property;
  try {
    property = bot.dom.getProperty(element, propName);
  } catch (e) {
    // Leaves property undefined or null
  }

  // 1- Call getAttribute if getProperty fails,
  // i.e. property is null or undefined.
  // This happens for event handlers in Firefox.
  // For example, calling getProperty for 'onclick' would
  // fail while getAttribute for 'onclick' will succeed and
  // return the JS code of the handler.
  //
  // 2- When property is an object we fall back to the
  // actual attribute instead.
  // See issue http://code.google.com/p/selenium/issues/detail?id=966
  if (!goog.isDefAndNotNull(property) || goog.isObject(property)) {
    value = bot.dom.getAttribute(element, attribute);
  } else {
    value = property;
  }

  // The empty string is a valid return value.
  return goog.isDefAndNotNull(value) ? value.toString() : null;
};

/**
 * Get the location of the element in page space, if it's displayed.
 *
 * @param {!Element} element The element to get the location for.
 * @return {goog.math.Rect} The bounding rectangle of the element.
 */
webdriver.atoms.element.getLocation = function(element) {
  if (!bot.dom.isShown(element)) {
    return null;
  }
  return goog.style.getBounds(element);
};

/**
 * @param {Node} element The element to use.
 * @return {boolean} Whether the element is in the HEAD tag.
 * @private
 */
webdriver.atoms.element.isInHead_ = function(element) {
  while (element) {
    if (element.tagName && element.tagName.toLowerCase() == 'head') {
      return true;
    }
    try {
      element = element.parentNode;
    } catch (e) {
      // Fine. the DOM has dispeared from underneath us
      return false;
    }
  }

  return false;
};

/**
 * @param {!Element} element The element to get the text from.
 * @return {string} The visible text or an empty string.
 */
webdriver.atoms.element.getText = function(element) {
  if (webdriver.atoms.element.isInHead_(element)) {
    var doc = goog.dom.getOwnerDocument(element);
    if (element.tagName.toUpperCase() == goog.dom.TagName.TITLE &&
        goog.dom.getWindow(doc) == bot.getWindow().top) {
      return goog.string.trim((/** @type {string} */doc.title));
    }
    return '';
  }

  return bot.dom.getVisibleText(element);
};

/**
 * Types keys on the given {@code element} with a virtual keyboard. Converts
 * special characters from the WebDriver JSON wire protocol to the appropriate
 * {@link bot.Keyboard.Key} value.
 *
 * @param {!Element} element The element to type upon.
 * @param {!Array.<string>} keys The keys to type on the element.
 * @param {bot.Keyboard=} opt_keyboard Keyboard to use; if not provided,
 *    constructs one.
 * @see bot.action.type
 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
 */
webdriver.atoms.element.type = function(element, keys, opt_keyboard) {
  // Convert to bot.Keyboard.Key values.
  /** @type {!Array.<!Array.<(string|!bot.Keyboard.Key)>>} */
  var convertedSequences = [];
  /** @type {!Array.<(string|!bot.Keyboard.Key)>} */
  var current = [];
  convertedSequences.push(current);

  goog.array.forEach(keys, function(sequence) {
    goog.array.forEach(sequence.split(''), function(key) {
      if (isWebDriverKey(key)) {
        var webdriverKey = webdriver.atoms.element.type.JSON_TO_KEY_MAP_[key];
        // goog.isNull uses ==, which accepts undefined.
        if (webdriverKey === null) {
          // bot.action.type does not support a "null" key, so we have to
          // terminate the entire sequence to release modifier keys.
          convertedSequences.push(current = []);
        } else if (goog.isDef(webdriverKey)) {
          current.push(webdriverKey);
        } else {
          throw Error('Unsupported WebDriver key: \\u' +
              key.charCodeAt(0).toString(16));
        }
      } else {
        // Handle common aliases.
        switch (key) {
          case '\n':
            current.push(bot.Keyboard.Keys.ENTER);
            break;
          case '\t':
            current.push(bot.Keyboard.Keys.TAB);
            break;
          case '\b':
            current.push(bot.Keyboard.Keys.BACKSPACE);
            break;
          default:
            current.push(key);
            break;
        }
      }
    });
  });

  goog.array.forEach(convertedSequences, function(sequence) {
    bot.action.type(element, sequence, opt_keyboard);
  });

  function isWebDriverKey(c) {
    return '\uE000' <= c && c <= '\uE03D';
  }
};

/**
 * Maps JSON wire protocol values to their {@link bot.Keyboard.Key} counterpart.
 * @type {!Object.<bot.Keyboard.Key>}
 * @const
 * @private
 */
webdriver.atoms.element.type.JSON_TO_KEY_MAP_ = {};
goog.scope(function() {
var map = webdriver.atoms.element.type.JSON_TO_KEY_MAP_;
var key = webdriver.Key;
var botKey = bot.Keyboard.Keys;

map[key.NULL] = null;
map[key.BACK_SPACE] = botKey.BACKSPACE;
map[key.TAB] = botKey.TAB;
map[key.RETURN] = botKey.ENTER;
// This not correct, but most browsers will do the right thing.
map[key.ENTER] = botKey.ENTER;
map[key.SHIFT] = botKey.SHIFT;
map[key.CONTROL] = botKey.CONTROL;
map[key.ALT] = botKey.ALT;
map[key.PAUSE] = botKey.PAUSE;
map[key.ESCAPE] = botKey.ESC;
map[key.SPACE] = botKey.SPACE;
map[key.PAGE_UP] = botKey.PAGE_UP;
map[key.PAGE_DOWN] = botKey.PAGE_DOWN;
map[key.END] = botKey.END;
map[key.HOME] = botKey.HOME;
map[key.LEFT] = botKey.LEFT;
map[key.UP] = botKey.UP;
map[key.RIGHT] = botKey.RIGHT;
map[key.DOWN] = botKey.DOWN;
map[key.INSERT] = botKey.INSERT;
map[key.DELETE] = botKey.DELETE;
map[key.SEMICOLON] = botKey.SEMICOLON;
map[key.EQUALS] = botKey.EQUALS;
map[key.NUMPAD0] = botKey.NUM_ZERO;
map[key.NUMPAD1] = botKey.NUM_ONE;
map[key.NUMPAD2] = botKey.NUM_TWO;
map[key.NUMPAD3] = botKey.NUM_THREE;
map[key.NUMPAD4] = botKey.NUM_FOUR;
map[key.NUMPAD5] = botKey.NUM_FIVE;
map[key.NUMPAD6] = botKey.NUM_SIX;
map[key.NUMPAD7] = botKey.NUM_SEVEN;
map[key.NUMPAD8] = botKey.NUM_EIGHT;
map[key.NUMPAD9] = botKey.NUM_NINE;
map[key.MULTIPLY] = botKey.NUM_MULTIPLY;
map[key.ADD] = botKey.NUM_PLUS;
map[key.SUBTRACT] = botKey.NUM_MINUS;
map[key.DECIMAL] = botKey.NUM_PERIOD;
map[key.DIVIDE] = botKey.NUM_DIVISION;
map[key.SEPARATOR] = botKey.SEPARATOR;
map[key.F1] = botKey.F1;
map[key.F2] = botKey.F2;
map[key.F3] = botKey.F3;
map[key.F4] = botKey.F4;
map[key.F5] = botKey.F5;
map[key.F6] = botKey.F6;
map[key.F7] = botKey.F7;
map[key.F8] = botKey.F8;
map[key.F9] = botKey.F9;
map[key.F10] = botKey.F10;
map[key.F11] = botKey.F11;
map[key.F12] = botKey.F12;
map[key.META] = botKey.META;
});  // goog.scope
<commit_msg>DanielWagnerHall: getAttribute always returns a defined value - don't bother checking against undefined<commit_after>// Copyright 2010 WebDriver committers
// Copyright 2010 Google Inc.
//
// 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.

/**
 * @fileoverview Atoms-based implementation of the webelement interface.
 */

goog.provide('webdriver.atoms.element');

goog.require('bot.Keyboard.Keys');
goog.require('bot.action');
goog.require('bot.dom');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.math');
goog.require('goog.string');
goog.require('goog.style');
goog.require('webdriver.Key');

/**
 * @param {!Element} element The element to use.
 * @return {boolean} Whether the element is checked or selected.
 */
webdriver.atoms.element.isSelected = function(element) {
  // Although this method looks unloved, its compiled form is used by
  // the Chrome and OperaDrivers.
  if (!bot.dom.isSelectable(element)) {
    return false;
  }

  return bot.dom.isSelected(element);
};

/**
 * Common aliases for properties. This maps names that users use to the correct
 * property name.
 *
 * @const
 * @private
 */
webdriver.atoms.element.PROPERTY_ALIASES_ = {
  'class': 'className',
  'readonly': 'readOnly'
};

/**
 * Used to determine whether we should return a boolean value from getAttribute.
 * These are all extracted from the WHATWG spec:
 *
 *   http://www.whatwg.org/specs/web-apps/current-work/
 *
 * These must all be lower-case.
 *
 * @const
 * @private
 */
webdriver.atoms.element.BOOLEAN_PROPERTIES_ = [
  'async',
  'autofocus',
  'autoplay',
  'checked',
  'compact',
  'complete',
  'controls',
  'declare',
  'defaultchecked',
  'defaultselected',
  'defer',
  'disabled',
  'draggable',
  'ended',
  'formnovalidate',
  'hidden',
  'indeterminate',
  'iscontenteditable',
  'ismap',
  'itemscope',
  'loop',
  'multiple',
  'muted',
  'nohref',
  'noresize',
  'noshade',
  'novalidate',
  'nowrap',
  'open',
  'paused',
  'pubdate',
  'readonly',
  'required',
  'reversed',
  'scoped',
  'seamless',
  'seeking',
  'selected',
  'spellcheck',
  'truespeed',
  'willvalidate'
];

/**
 * Get the value of the given property or attribute. If the "attribute" is for
 * a boolean property, we return null in the case where the value is false. If
 * the attribute name is "style" an attempt to convert that style into a string
 * is done.
 *
 * @param {!Element} element The element to use.
 * @param {string} attribute The name of the attribute to look up.
 * @return {?string} The string value of the attribute or property, or null.
 */
webdriver.atoms.element.getAttribute = function(element, attribute) {
  var value = null;
  var name = attribute.toLowerCase();

  if ('style' == attribute.toLowerCase()) {
    value = element.style;

    if (value && !goog.isString(value)) {
      value = value.cssText;
    }

    return (/** @type {?string} */value);
  }

  if (('selected' == name || 'checked' == name) &&
      bot.dom.isSelectable(element)) {
    return bot.dom.isSelected(element) ? 'true' : null;
  }

  // Our tests suggest that returning the attribute is desirable for
  // the href attribute of <a> tags and the src attribute of <img> tags,
  // but we normally attempt to get the property value before the attribute.
  var isLink = bot.dom.isElement(element, goog.dom.TagName.A);
  var isImg = bot.dom.isElement(element, goog.dom.TagName.IMG);

  // Although the attribute matters, the property is consistent. Return that in
  // preference to the attribute for links and images.
  if ((isImg && name == 'src') || (isLink && name == 'href')) {
    value = bot.dom.getAttribute(element, name);
    if (value) {
      // We want the full URL if present
      value = bot.dom.getProperty(element, name);
    }
    return (/** @type {?string} */value);
  }

  var propName = webdriver.atoms.element.PROPERTY_ALIASES_[attribute] ||
      attribute;
  if (goog.array.contains(webdriver.atoms.element.BOOLEAN_PROPERTIES_, name)) {
    value = !goog.isNull(bot.dom.getAttribute(element, attribute)) ||
        bot.dom.getProperty(element, propName);
    return value ? 'true' : null;
  }

  var property;
  try {
    property = bot.dom.getProperty(element, propName);
  } catch (e) {
    // Leaves property undefined or null
  }

  // 1- Call getAttribute if getProperty fails,
  // i.e. property is null or undefined.
  // This happens for event handlers in Firefox.
  // For example, calling getProperty for 'onclick' would
  // fail while getAttribute for 'onclick' will succeed and
  // return the JS code of the handler.
  //
  // 2- When property is an object we fall back to the
  // actual attribute instead.
  // See issue http://code.google.com/p/selenium/issues/detail?id=966
  if (!goog.isDefAndNotNull(property) || goog.isObject(property)) {
    value = bot.dom.getAttribute(element, attribute);
  } else {
    value = property;
  }

  // The empty string is a valid return value.
  return goog.isDefAndNotNull(value) ? value.toString() : null;
};

/**
 * Get the location of the element in page space, if it's displayed.
 *
 * @param {!Element} element The element to get the location for.
 * @return {goog.math.Rect} The bounding rectangle of the element.
 */
webdriver.atoms.element.getLocation = function(element) {
  if (!bot.dom.isShown(element)) {
    return null;
  }
  return goog.style.getBounds(element);
};

/**
 * @param {Node} element The element to use.
 * @return {boolean} Whether the element is in the HEAD tag.
 * @private
 */
webdriver.atoms.element.isInHead_ = function(element) {
  while (element) {
    if (element.tagName && element.tagName.toLowerCase() == 'head') {
      return true;
    }
    try {
      element = element.parentNode;
    } catch (e) {
      // Fine. the DOM has dispeared from underneath us
      return false;
    }
  }

  return false;
};

/**
 * @param {!Element} element The element to get the text from.
 * @return {string} The visible text or an empty string.
 */
webdriver.atoms.element.getText = function(element) {
  if (webdriver.atoms.element.isInHead_(element)) {
    var doc = goog.dom.getOwnerDocument(element);
    if (element.tagName.toUpperCase() == goog.dom.TagName.TITLE &&
        goog.dom.getWindow(doc) == bot.getWindow().top) {
      return goog.string.trim((/** @type {string} */doc.title));
    }
    return '';
  }

  return bot.dom.getVisibleText(element);
};

/**
 * Types keys on the given {@code element} with a virtual keyboard. Converts
 * special characters from the WebDriver JSON wire protocol to the appropriate
 * {@link bot.Keyboard.Key} value.
 *
 * @param {!Element} element The element to type upon.
 * @param {!Array.<string>} keys The keys to type on the element.
 * @param {bot.Keyboard=} opt_keyboard Keyboard to use; if not provided,
 *    constructs one.
 * @see bot.action.type
 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
 */
webdriver.atoms.element.type = function(element, keys, opt_keyboard) {
  // Convert to bot.Keyboard.Key values.
  /** @type {!Array.<!Array.<(string|!bot.Keyboard.Key)>>} */
  var convertedSequences = [];
  /** @type {!Array.<(string|!bot.Keyboard.Key)>} */
  var current = [];
  convertedSequences.push(current);

  goog.array.forEach(keys, function(sequence) {
    goog.array.forEach(sequence.split(''), function(key) {
      if (isWebDriverKey(key)) {
        var webdriverKey = webdriver.atoms.element.type.JSON_TO_KEY_MAP_[key];
        // goog.isNull uses ==, which accepts undefined.
        if (webdriverKey === null) {
          // bot.action.type does not support a "null" key, so we have to
          // terminate the entire sequence to release modifier keys.
          convertedSequences.push(current = []);
        } else if (goog.isDef(webdriverKey)) {
          current.push(webdriverKey);
        } else {
          throw Error('Unsupported WebDriver key: \\u' +
              key.charCodeAt(0).toString(16));
        }
      } else {
        // Handle common aliases.
        switch (key) {
          case '\n':
            current.push(bot.Keyboard.Keys.ENTER);
            break;
          case '\t':
            current.push(bot.Keyboard.Keys.TAB);
            break;
          case '\b':
            current.push(bot.Keyboard.Keys.BACKSPACE);
            break;
          default:
            current.push(key);
            break;
        }
      }
    });
  });

  goog.array.forEach(convertedSequences, function(sequence) {
    bot.action.type(element, sequence, opt_keyboard);
  });

  function isWebDriverKey(c) {
    return '\uE000' <= c && c <= '\uE03D';
  }
};

/**
 * Maps JSON wire protocol values to their {@link bot.Keyboard.Key} counterpart.
 * @type {!Object.<bot.Keyboard.Key>}
 * @const
 * @private
 */
webdriver.atoms.element.type.JSON_TO_KEY_MAP_ = {};
goog.scope(function() {
var map = webdriver.atoms.element.type.JSON_TO_KEY_MAP_;
var key = webdriver.Key;
var botKey = bot.Keyboard.Keys;

map[key.NULL] = null;
map[key.BACK_SPACE] = botKey.BACKSPACE;
map[key.TAB] = botKey.TAB;
map[key.RETURN] = botKey.ENTER;
// This not correct, but most browsers will do the right thing.
map[key.ENTER] = botKey.ENTER;
map[key.SHIFT] = botKey.SHIFT;
map[key.CONTROL] = botKey.CONTROL;
map[key.ALT] = botKey.ALT;
map[key.PAUSE] = botKey.PAUSE;
map[key.ESCAPE] = botKey.ESC;
map[key.SPACE] = botKey.SPACE;
map[key.PAGE_UP] = botKey.PAGE_UP;
map[key.PAGE_DOWN] = botKey.PAGE_DOWN;
map[key.END] = botKey.END;
map[key.HOME] = botKey.HOME;
map[key.LEFT] = botKey.LEFT;
map[key.UP] = botKey.UP;
map[key.RIGHT] = botKey.RIGHT;
map[key.DOWN] = botKey.DOWN;
map[key.INSERT] = botKey.INSERT;
map[key.DELETE] = botKey.DELETE;
map[key.SEMICOLON] = botKey.SEMICOLON;
map[key.EQUALS] = botKey.EQUALS;
map[key.NUMPAD0] = botKey.NUM_ZERO;
map[key.NUMPAD1] = botKey.NUM_ONE;
map[key.NUMPAD2] = botKey.NUM_TWO;
map[key.NUMPAD3] = botKey.NUM_THREE;
map[key.NUMPAD4] = botKey.NUM_FOUR;
map[key.NUMPAD5] = botKey.NUM_FIVE;
map[key.NUMPAD6] = botKey.NUM_SIX;
map[key.NUMPAD7] = botKey.NUM_SEVEN;
map[key.NUMPAD8] = botKey.NUM_EIGHT;
map[key.NUMPAD9] = botKey.NUM_NINE;
map[key.MULTIPLY] = botKey.NUM_MULTIPLY;
map[key.ADD] = botKey.NUM_PLUS;
map[key.SUBTRACT] = botKey.NUM_MINUS;
map[key.DECIMAL] = botKey.NUM_PERIOD;
map[key.DIVIDE] = botKey.NUM_DIVISION;
map[key.SEPARATOR] = botKey.SEPARATOR;
map[key.F1] = botKey.F1;
map[key.F2] = botKey.F2;
map[key.F3] = botKey.F3;
map[key.F4] = botKey.F4;
map[key.F5] = botKey.F5;
map[key.F6] = botKey.F6;
map[key.F7] = botKey.F7;
map[key.F8] = botKey.F8;
map[key.F9] = botKey.F9;
map[key.F10] = botKey.F10;
map[key.F11] = botKey.F11;
map[key.F12] = botKey.F12;
map[key.META] = botKey.META;
});  // goog.scope
<|endoftext|>


<commit_before>$(document).ready(function() {

    // Raleway doesn't load after being redirected from another page; redeclaring the font-face fixes that.
    let font = "<style> @@font-face{ font-family: 'raleway'; src: url('/assets/fonts/Raleway/Raleway-Regular.ttf');} </style>";
    $("head").append(font);

    // Adjusts html padding depending if the orientation of the phone is vertical or horizontal.
    function orientationCheck(){
        if(window.innerHeight > window.innerWidth){
            $('html').css('padding-top','10px');
        } else {
            $('html').css('padding-top','0px');
        }
    }

    window.addEventListener("resize", function() {
        orientationCheck();
    }, false);

    orientationCheck();

    resizeMobileValidation();

    //add the 'animate-button-0' class to all validaton buttons so an animation is performed on click
    document.getElementById("validation-agree-button-0").classList.add("animate-button-0");
    document.getElementById("validation-not-sure-button-0").classList.add("animate-button-0");
    document.getElementById("validation-disagree-button-0").classList.add("animate-button-0");

    // If the site is loaded in landscape mode first, 'loadedScreenLandscape' will be set to true
    // and when the screen is flipped back to portrait mode the site will be reloaded to set the panoramas
    // correctly.
    let loadedScreenLandscape = false;

    if (orientation != 0) {
        svv.modalLandscape.show();
        loadedScreenLandscape = true;
    } else {
        svv.modalLandscape.hide();
    }

    $(window).on('orientationchange', function (event) {
        if (orientation != 0) {
            svv.modalLandscape.show();
        } else if (loadedScreenLandscape) {
            location.reload();
        } else {
            svv.modalLandscape.hide();
        }
    });
});

// Prevents double tap functionality. We only want to pinch zoom in GSV.
let doubleTouchStartTimestamp = 0;
document.addEventListener("touchstart", function(event){
    let now = +(new Date());
    if (doubleTouchStartTimestamp + 500 > now){
        event.preventDefault();
    }

    doubleTouchStartTimestamp = now;
}, {passive: false});

/**
 * Resizes html elements based on phone size.
 */
function resizeMobileValidation() {
    let h = window.innerHeight;
    let w = window.innerWidth;

    // Change validation button position.
    document.getElementById("validation-button-holder").style.top = h - 200 + "px";
    document.getElementById("validation-button-holder").style.width = w + "px";
    document.getElementById("validation-agree-button-0").style.width = w / 3 - 30 + "px";
    document.getElementById("validation-disagree-button-0").style.width = w / 3 - 30 + "px";
    document.getElementById("validation-not-sure-button-0").style.width = w / 3 - 30 + "px";
    // Scale progress bar.
    document.getElementById("status-current-mission-completion-bar").style.width = w + "px";
    document.getElementById("status-current-mission-completion-bar").style.width = w + "px";
    document.getElementById("status-current-mission-completion-rate").style.width = w + "px";
    // Resize button elements.
    document.getElementById("title-bar-holder").style.width = w + "px";
    document.getElementById("left-column-jump-button").style.left = w - 75 + "px";
    document.getElementById("info-button").style.left = w - 150 + "px";
    // Resize mission start popup.
    let modalForegrounds = document.getElementsByClassName("modal-foreground");
    for (let i = 0; i < modalForegrounds.length; i++) {
        modalForegrounds[i].style.width = w + "px";
        modalForegrounds[i].style.height = h + "px";
    }
    document.getElementById("ps-logo-mission").style.width = w - 70 + 'px';
    document.getElementById("ps-logo-mission-complete").style.width = w - 70 + 'px';
    // Resize modal info panel.
    let exampleImages = document.getElementsByClassName("example-image");
    for (let i = 0; i < exampleImages.length; i++) {
        exampleImages[i].style.width = w / 2 - 50 + "px";
        exampleImages[i].style.height = (w / 2 - 50) * (83 / 95) + "px";
    }
    let statusBoxes = document.getElementsByClassName("status-box");
    for (let i = 0; i < statusBoxes.length; i++) {
        statusBoxes[i].style.height = w * (83 / 95) / 2 + 20 + "px";
    }
    document.getElementById("modal-info-close-button").style.left = w - 75 + "px";

    // If you need to do functional testing without a phone, enable this code to test using Chrome's inspection tool.
    // document.getElementById("validation-button-holder").style.top = h - 1500 + "px";
    // document.getElementById("validation-button-holder").style.width = w + "px";
    // document.getElementById("validation-agree-button-0").style.width = w/5-30 + "px";
    // document.getElementById("validation-disagree-button-0").style.width = w/5-30 + "px";
    // document.getElementById("validation-not-sure-button-0").style.width = w/5-30 + "px";
    // document.getElementById("left-column-jump-button").style.left = w - 600 + "px";
    // document.getElementById("info-button").style.left = w - 700 + "px";
}
<commit_msg>Updated comments<commit_after>$(document).ready(function() {

    // Raleway doesn't load after being redirected from another page; redeclaring the font-face fixes that.
    let font = "<style> @@font-face{ font-family: 'raleway'; src: url('/assets/fonts/Raleway/Raleway-Regular.ttf');} </style>";
    $("head").append(font);

    // Adjusts html padding depending if the orientation of the phone is vertical or horizontal.
    function orientationCheck(){
        if(window.innerHeight > window.innerWidth){
            $('html').css('padding-top','10px');
        } else {
            $('html').css('padding-top','0px');
        }
    }

    window.addEventListener("resize", function() {
        orientationCheck();
    }, false);

    orientationCheck();

    resizeMobileValidation();

    // Add the 'animate-button-0' class to all validaton buttons so an animation is performed to
    // confirm click.
    document.getElementById("validation-agree-button-0").classList.add("animate-button-0");
    document.getElementById("validation-not-sure-button-0").classList.add("animate-button-0");
    document.getElementById("validation-disagree-button-0").classList.add("animate-button-0");

    // If the site is loaded in landscape mode first, 'loadedScreenLandscape' will be set to true
    // and when the screen is flipped back to portrait mode the site will be reloaded to set the panoramas
    // correctly.
    let loadedScreenLandscape = false;

    if (orientation != 0) {
        svv.modalLandscape.show();
        loadedScreenLandscape = true;
    } else {
        svv.modalLandscape.hide();
    }

    $(window).on('orientationchange', function (event) {
        if (orientation != 0) {
            svv.modalLandscape.show();
        } else if (loadedScreenLandscape) {
            location.reload();
        } else {
            svv.modalLandscape.hide();
        }
    });
});

// Prevents double tap functionality. We only want to pinch zoom in GSV.
let doubleTouchStartTimestamp = 0;
document.addEventListener("touchstart", function(event){
    let now = +(new Date());
    if (doubleTouchStartTimestamp + 500 > now){
        event.preventDefault();
    }

    doubleTouchStartTimestamp = now;
}, {passive: false});

/**
 * Resizes html elements based on phone size.
 */
function resizeMobileValidation() {
    let h = window.innerHeight;
    let w = window.innerWidth;

    // Change validation button position.
    document.getElementById("validation-button-holder").style.top = h - 200 + "px";
    document.getElementById("validation-button-holder").style.width = w + "px";
    document.getElementById("validation-agree-button-0").style.width = w / 3 - 30 + "px";
    document.getElementById("validation-disagree-button-0").style.width = w / 3 - 30 + "px";
    document.getElementById("validation-not-sure-button-0").style.width = w / 3 - 30 + "px";
    // Scale progress bar.
    document.getElementById("status-current-mission-completion-bar").style.width = w + "px";
    document.getElementById("status-current-mission-completion-bar").style.width = w + "px";
    document.getElementById("status-current-mission-completion-rate").style.width = w + "px";
    // Resize button elements.
    document.getElementById("title-bar-holder").style.width = w + "px";
    document.getElementById("left-column-jump-button").style.left = w - 75 + "px";
    document.getElementById("info-button").style.left = w - 150 + "px";
    // Resize mission start popup.
    let modalForegrounds = document.getElementsByClassName("modal-foreground");
    for (let i = 0; i < modalForegrounds.length; i++) {
        modalForegrounds[i].style.width = w + "px";
        modalForegrounds[i].style.height = h + "px";
    }
    document.getElementById("ps-logo-mission").style.width = w - 70 + 'px';
    document.getElementById("ps-logo-mission-complete").style.width = w - 70 + 'px';
    // Resize modal info panel.
    let exampleImages = document.getElementsByClassName("example-image");
    for (let i = 0; i < exampleImages.length; i++) {
        exampleImages[i].style.width = w / 2 - 50 + "px";
        exampleImages[i].style.height = (w / 2 - 50) * (83 / 95) + "px";
    }
    let statusBoxes = document.getElementsByClassName("status-box");
    for (let i = 0; i < statusBoxes.length; i++) {
        statusBoxes[i].style.height = w * (83 / 95) / 2 + 20 + "px";
    }
    document.getElementById("modal-info-close-button").style.left = w - 75 + "px";

    // If you need to do functional testing without a phone, enable this code to test using Chrome's inspection tool.
    // document.getElementById("validation-button-holder").style.top = h - 1500 + "px";
    // document.getElementById("validation-button-holder").style.width = w + "px";
    // document.getElementById("validation-agree-button-0").style.width = w/5-30 + "px";
    // document.getElementById("validation-disagree-button-0").style.width = w/5-30 + "px";
    // document.getElementById("validation-not-sure-button-0").style.width = w/5-30 + "px";
    // document.getElementById("left-column-jump-button").style.left = w - 600 + "px";
    // document.getElementById("info-button").style.left = w - 700 + "px";
}
<|endoftext|>


<commit_before>import {Rect, Point, Vector2d, PositionVector} from '../2dGameUtils';
import {mixin, ObservableMixin} from '../2dGameUtils';
import {Log} from '../2dGameUtils';
import {CollisionResolver} from './collision';

/* Physics module
 * The key functionality of this module, is that given a set of control inputs,
 * a movement vector and list of collisions that occurred is output.
 */

const zeroVector = new Vector2d();

const maxSpeed = 5;

function mod(v, m) {
    const r = v % m;
    return r < 0 ? r + m : r;
}

export class PhysicsSystem {
    constructor(dimensionsRect=null) {
        this.collisionResolver = new CollisionResolver();
        this.models = [];
        if (dimensionsRect !== null) this.setBoundaries(dimensionsRect);
    }

    setBoundaries(dimensionsRect) {
        // Boundaries must all point in clockwise direction as
        // various routines depend on this for optimisations.
        // The Rect class does ensure that the points are oriented
        // correctly for this to be always true.
        this.boundaries = [
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x1, dimensionsRect.y1),
                new Point(dimensionsRect.x2, dimensionsRect.y1)
            ),
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x2, dimensionsRect.y1),
                new Point(dimensionsRect.x2, dimensionsRect.y2)
            ),
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x2, dimensionsRect.y2),
                new Point(dimensionsRect.x1, dimensionsRect.y2)
            ),
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x1, dimensionsRect.y2),
                new Point(dimensionsRect.x1, dimensionsRect.y1)
            )
        ];

        this.collisionResolver.clearBoundaries();
        this.boundaries.forEach(line => this.collisionResolver.registerBoundary(line));
    }

    getBoundary(name) {
        switch (name) {
            case 'top': return this.boundaries[0];
            case 'bottom': return this.boundaries[2];
            case 'left': return this.boundaries[1];
            case 'right': return this.boundaries[3];
        }
        return null;
    }

    add(model) {
        this.models.push(model);
        this.collisionResolver.registerPhysicsModel(model);
    }

    update(timeDelta) {
        for (let model of this.models) {
            model.update(timeDelta);
        }

        this.collisionResolver.update(timeDelta);

        // move the objects
        for (let model of this.models) {
            model.move();
        }
    }
}

export class Thruster {
    constructor(power, angle) {
        this._thrustVector = Vector2d.createFromAngle(angle, power);
        this.isFiring = false;
    }

    set firing(isFiring) {
        this.isFiring = isFiring === true;
    }

    get thrustVector() {
        if (this.isFiring) {
            return this._thrustVector;
        }
        return zeroVector;
    }
}

class MotionTracker extends PositionVector {
    constructor(position, vector=null) {
        // Tracks position and velocity via PositionVector.
        if (vector !== null)
            super(position.x, position.y, vector.x, vector.y);
        else
            super(position.x, position.y);

        // Also tracks what portion of a timeslice this motion
        // accounts for - as due to collisions a model may have
        // multiple changes of trajectory.
        this.time = 1.0;
    }
}

let physicsModelCount = 0;

export class PhysicsModel {
    constructor(system, boundingCircleR, mass, position, motion=null) {
        this.system = system;
        this.motion = new MotionTracker(position, motion);
        this.boundingCircleR = boundingCircleR;
        this.mass = mass;
        this.thrusters = [];
        this.rotateRate = 10;
        this.rotateDirection = 0;
        this.rotateAngle = 0;
        this.otherForce = [];
        this.collidable = true;
        this.system.add(this);
        this.name = `PhysicsModel_${physicsModelCount++}`;
    }

    createThruster(power, angle_vector) {
        const thruster = new Thruster(power, angle_vector);
        this.thrusters.push(thruster);
        return thruster;
    }

    update(timeDelta) {
        Log.write('physicsUpdate', this.name);

        Log.write('position', this.motion.position);

        const forceVector = new Vector2d();

        this.motion.time = 1.0;

        this.otherForce.forEach(e => {
            Log.write('externalForce', e.vector);
            forceVector.add(e.vector);
            e.duration -= timeDelta;
        });
        this.otherForce = this.otherForce.filter(e => e.duration > 0);

        for (let thruster of this.thrusters) {
            Log.write('thruster', thruster.thrustVector);
            forceVector.add(thruster.thrustVector)
        }

        this.rotateAngle += (this.rotateDirection/ (this.rotateRate / timeDelta));
        forceVector.rotate(this.rotateAngle);

        if (forceVector.length !== 0) {
            forceVector.divide(this.mass);
            forceVector.divide(1000 / timeDelta);

            Log.write('forceVector', forceVector);

            this.motion.vector.add(forceVector);

            if (this.motion.vector.length > maxSpeed) {
                this.motion.vector.normalise().multiply(maxSpeed);
            }

            Log.write('motion', this.motion);
        }

        Log.write('endPhysicsUpdate');
    }

    move() {
        Log.write('move', this.name);
        if (this.motion.vector.length > maxSpeed)
            this.motion.vector.normalise().multiply(maxSpeed);

        Log.write('motion', this.motion);
        this.motion.position.translate(this.motion.vector.copy().multiply(this.motion.time));
        Log.write('position', this.motion.position);
    }

    stop() {
        this.motion.vector.zero();
    }

    rotate(direction) {
        this.rotateDirection = direction;
    }

    get position() {
        return this.motion.position;
    }

    get moveVector() {
        return this.motion.vector;
    }

    thruster(i) {
        return this.thrusters[i];
    }

    addExternalForce(v, duration) {
        this.otherForce.push({"vector": v, "duration": duration});
    }
}
mixin(PhysicsModel, ObservableMixin);

class PhysicModelLoader {
    newPhysicsModel(config) {
        // get some variables from the asset file
        // const pm = new PhysicsModel();
        // set up all other paramters via functions
        //return pm;
    }
}
<commit_msg>Only apply rotation to forces that original from the physics model<commit_after>import {Rect, Point, Vector2d, PositionVector} from '../2dGameUtils';
import {mixin, ObservableMixin} from '../2dGameUtils';
import {Log} from '../2dGameUtils';
import {CollisionResolver} from './collision';

/* Physics module
 * The key functionality of this module, is that given a set of control inputs,
 * a movement vector and list of collisions that occurred is output.
 */

const zeroVector = new Vector2d();

const maxSpeed = 5;

function mod(v, m) {
    const r = v % m;
    return r < 0 ? r + m : r;
}

export class PhysicsSystem {
    constructor(dimensionsRect=null) {
        this.collisionResolver = new CollisionResolver();
        this.models = [];
        if (dimensionsRect !== null) this.setBoundaries(dimensionsRect);
    }

    setBoundaries(dimensionsRect) {
        // Boundaries must all point in clockwise direction as
        // various routines depend on this for optimisations.
        // The Rect class does ensure that the points are oriented
        // correctly for this to be always true.
        this.boundaries = [
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x1, dimensionsRect.y1),
                new Point(dimensionsRect.x2, dimensionsRect.y1)
            ),
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x2, dimensionsRect.y1),
                new Point(dimensionsRect.x2, dimensionsRect.y2)
            ),
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x2, dimensionsRect.y2),
                new Point(dimensionsRect.x1, dimensionsRect.y2)
            ),
            PositionVector.createFromPoints(
                new Point(dimensionsRect.x1, dimensionsRect.y2),
                new Point(dimensionsRect.x1, dimensionsRect.y1)
            )
        ];

        this.collisionResolver.clearBoundaries();
        this.boundaries.forEach(line => this.collisionResolver.registerBoundary(line));
    }

    getBoundary(name) {
        switch (name) {
            case 'top': return this.boundaries[0];
            case 'bottom': return this.boundaries[2];
            case 'left': return this.boundaries[1];
            case 'right': return this.boundaries[3];
        }
        return null;
    }

    add(model) {
        this.models.push(model);
        this.collisionResolver.registerPhysicsModel(model);
    }

    update(timeDelta) {
        for (let model of this.models) {
            model.update(timeDelta);
        }

        this.collisionResolver.update(timeDelta);

        // move the objects
        for (let model of this.models) {
            model.move();
        }
    }
}

export class Thruster {
    constructor(power, angle) {
        this._thrustVector = Vector2d.createFromAngle(angle, power);
        this.isFiring = false;
    }

    set firing(isFiring) {
        this.isFiring = isFiring === true;
    }

    get thrustVector() {
        if (this.isFiring) {
            return this._thrustVector;
        }
        return zeroVector;
    }
}

class MotionTracker extends PositionVector {
    constructor(position, vector=null) {
        // Tracks position and velocity via PositionVector.
        if (vector !== null)
            super(position.x, position.y, vector.x, vector.y);
        else
            super(position.x, position.y);

        // Also tracks what portion of a timeslice this motion
        // accounts for - as due to collisions a model may have
        // multiple changes of trajectory.
        this.time = 1.0;
    }
}

let physicsModelCount = 0;

export class PhysicsModel {
    constructor(system, boundingCircleR, mass, position, motion=null) {
        this.system = system;
        this.motion = new MotionTracker(position, motion);
        this.boundingCircleR = boundingCircleR;
        this.mass = mass;
        this.thrusters = [];
        this.rotateRate = 10;
        this.rotateDirection = 0;
        this.rotateAngle = 0;
        this.otherForce = [];
        this.collidable = true;
        this.system.add(this);
        this.name = `PhysicsModel_${physicsModelCount++}`;
    }

    createThruster(power, angle_vector) {
        const thruster = new Thruster(power, angle_vector);
        this.thrusters.push(thruster);
        return thruster;
    }

    update(timeDelta) {
        Log.write('physicsUpdate', this.name);

        Log.write('position', this.motion.position);

        const forceVector = new Vector2d();

        this.motion.time = 1.0;

        // Apply forces exerted by this object - will be affected by
        // rotation angle.
        for (let thruster of this.thrusters) {
            Log.write('thruster', thruster.thrustVector);
            forceVector.add(thruster.thrustVector)
        }

        this.rotateAngle += (this.rotateDirection/ (this.rotateRate / timeDelta));
        forceVector.rotate(this.rotateAngle);

        // Apply forces coming from external sources - not affected by rotation.
        this.otherForce.forEach(e => {
            Log.write('externalForce', e.vector);
            forceVector.add(e.vector);
            e.duration -= timeDelta;
        });
        this.otherForce = this.otherForce.filter(e => e.duration > 0);

        if (forceVector.length !== 0) {
            forceVector.divide(this.mass);
            forceVector.divide(1000 / timeDelta);

            Log.write('forceVector', forceVector);

            this.motion.vector.add(forceVector);

            if (this.motion.vector.length > maxSpeed) {
                this.motion.vector.normalise().multiply(maxSpeed);
            }

            Log.write('motion', this.motion);
        }

        Log.write('endPhysicsUpdate');
    }

    move() {
        Log.write('move', this.name);
        if (this.motion.vector.length > maxSpeed)
            this.motion.vector.normalise().multiply(maxSpeed);

        Log.write('motion', this.motion);
        this.motion.position.translate(this.motion.vector.copy().multiply(this.motion.time));
        Log.write('position', this.motion.position);
    }

    stop() {
        this.motion.vector.zero();
    }

    rotate(direction) {
        this.rotateDirection = direction;
    }

    get position() {
        return this.motion.position;
    }

    get moveVector() {
        return this.motion.vector;
    }

    thruster(i) {
        return this.thrusters[i];
    }

    addExternalForce(v, duration) {
        this.otherForce.push({"vector": v, "duration": duration});
    }
}
mixin(PhysicsModel, ObservableMixin);

class PhysicModelLoader {
    newPhysicsModel(config) {
        // get some variables from the asset file
        // const pm = new PhysicsModel();
        // set up all other paramters via functions
        //return pm;
    }
}
<|endoftext|>


<commit_before>define(["config",
        "app/app",
        "ember",
        "lodash",
        "components/Pagination"], function(config, App, Ember, _) {
  "use strict";

  // "Abstract" generic controller for timelines
  App.TimelineGenericController = Ember.Controller.extend(App.Pagination, {
    selectFeedsOnCreate: true,
    attachFilesOnCreate: true,

    // NOTE: this code doesn't work reliably, see
    // https://github.com/emberjs/ember.js/issues/10343
    //postSortProperties: ['createdAt:desc'],
    //posts: Ember.computed.sort('model.posts', 'postSortProperties'),

    // 'attachments' should be an instance property (set on init), not a prototype property
    setupAttachments: function() {
      this.set('attachments', []);
    }.on('init'),

    didRequestRange: function(options) {
      this.transitionToRoute({ queryParams: { offset: options.offset } })
    },

    myFeed: function() {
      return (this.get('model.user.id') == this.get('session.currentUser.id')
              && this.get('model.user.isUser'))
        || (this.get('isSubscribed'))
    }.property('model.user.id', 'session.currentUser.id', 'isSubscribed'),

    isSubscribed: function() {
      var currentUser = this.get('session.currentUser')
      if (!currentUser) { return false }

      return (this.get('model.subscribers').isAny('id', currentUser.get('id')) && this.get('model.user.isGroup'))
    }.property('model.subscribers', 'session.currentUser.id'),

    isAdmin: function() {
      var adminIds = this.get('model.user.administratorIds')
      var currentUserId = this.get('session.currentUser.id')

      return adminIds && adminIds.indexOf(currentUserId) !== -1
    }.property('session.currentUser.id'),

    isAttachmentsVisible: false,
    isSendToVisible: false,

    actions: {
      showAttachments: function() {
        this.toggleProperty('isAttachmentsVisible')
      },

      subscribe: function() {
        var user = this.get('model.user')
        Ember.$.ajax({
          url: config.host + '/v1/users/' + user.get('username') + '/subscribe',
          type: 'post',
          context: this
        })
          .then(function(response) {
            var user = this.store.recordForId('user', response.users.id)
            this.store.pushPayload('user', response)
            var subscriberResponse = response
            subscriberResponse.subscribers = subscriberResponse.users
            this.store.pushPayload('subscriber', subscriberResponse)
            var subscriber = this.store.recordForId('subscriber', response.users.id)

            this.get('session').set('currentUser', this.store.getById('user', response.users.id))
            this.incrementProperty('model.user.statistics.subscribers')
            this.get('model.subscribers').addObject(subscriber)
          })
      },

      unsubscribe: function() {
        var user = this.get('model.user')
        Ember.$.ajax({
          url: config.host + '/v1/users/' + user.get('username') + '/unsubscribe',
          type: 'post',
          context: this
        })
          .then(function(response) {
            var user = this.store.recordForId('user', response.users.id)
            this.store.pushPayload('user', response)
            var subscriberResponse = response
            subscriberResponse.subscribers = subscriberResponse.users
            this.store.pushPayload('subscriber', subscriberResponse)
            var subscriber = this.store.recordForId('subscriber', response.users.id)

            this.get('session').set('currentUser', this.store.getById('user', response.users.id))
            this.decrementProperty('model.user.statistics.subscribers')
            this.get('model.subscribers').removeObject(subscriber)
          })
      },

      addAttachment: function(file) {
        // Create an attachment record
        var attachment = this.store.createRecord('attachment', {
          file: file
        })

        // Add a throbber (placeholder object, to show uploading progress)
        var attachmentList = this.get('attachments')
        var throbber = this.store.createRecord('attachment', { thumbnailUrl: '/img/throbber-100.gif' })
        var throbberIndex = attachmentList.length
        attachmentList.pushObject(throbber)

        // Save the attachment record to the backend
        attachment.save()
          .then(function(attachment) {
            // Replace the throbber with a real record
            attachmentList.replace(throbberIndex, 1, [ attachment ])
          })
      },

      create: function() {
        // Allow/disallow selecting feeds
        var feeds
        if (this.get('selectFeedsOnCreate')) {
          feeds = Ember.$('#sendToSelect').val()
        } else {
          feeds = this.get('model.user.username')
        }

        // Create a post record
        var post = this.store.createRecord('post', {
          body: this.get('body'),
          feeds: feeds
        })

        // Attach the attachments (without throbbers, if any)
        var attachmentList = this.get('attachments')
          .filter(function(item) {
            return !!item.id
          })
        post.get('attachments').pushObjects(attachmentList)

        // Clear the form
        this.set('body', '')
        this.set('attachments', [])
        this.set('isAttachmentsVisible', false)
        this.set('isSendToVisible', false)

        // Save it to the backend
        var that = this
        post.save()
          .then(function(post) {
            var object = that.get('model.posts').findProperty('id', post.get('id'))
            if (!object) {
              that.get('model.posts').unshiftObject(post)
            }
          })
      }
    }
  })
})
<commit_msg>Fixes subscriptions.<commit_after>define(["config",
        "app/app",
        "ember",
        "lodash",
        "components/Pagination"], function(config, App, Ember, _) {
  "use strict";

  // "Abstract" generic controller for timelines
  App.TimelineGenericController = Ember.Controller.extend(App.Pagination, {
    selectFeedsOnCreate: true,
    attachFilesOnCreate: true,

    // NOTE: this code doesn't work reliably, see
    // https://github.com/emberjs/ember.js/issues/10343
    //postSortProperties: ['createdAt:desc'],
    //posts: Ember.computed.sort('model.posts', 'postSortProperties'),

    // 'attachments' should be an instance property (set on init), not a prototype property
    setupAttachments: function() {
      this.set('attachments', []);
    }.on('init'),

    didRequestRange: function(options) {
      this.transitionToRoute({ queryParams: { offset: options.offset } })
    },

    myFeed: function() {
      return (this.get('model.user.id') == this.get('session.currentUser.id')
              && this.get('model.user.isUser'))
        || (this.get('isSubscribed'))
    }.property('model.user.id', 'session.currentUser.id', 'isSubscribed'),

    isSubscribed: function() {
      var currentUser = this.get('session.currentUser')
      if (!currentUser) { return false }

      return (this.get('model.subscribers').isAny('id', currentUser.get('id')))
    }.property('model.subscribers', 'session.currentUser.id'),

    isAdmin: function() {
      var adminIds = this.get('model.user.administratorIds')
      var currentUserId = this.get('session.currentUser.id')

      return adminIds && adminIds.indexOf(currentUserId) !== -1
    }.property('session.currentUser.id'),

    isAttachmentsVisible: false,
    isSendToVisible: false,

    actions: {
      showAttachments: function() {
        this.toggleProperty('isAttachmentsVisible')
      },

      subscribe: function() {
        var user = this.get('model.user')
        Ember.$.ajax({
          url: config.host + '/v1/users/' + user.get('username') + '/subscribe',
          type: 'post',
          context: this
        })
          .then(function(response) {
            var user = this.store.recordForId('user', response.users.id)
            this.store.pushPayload('user', response)
            var subscriberResponse = response
            subscriberResponse.subscribers = subscriberResponse.users
            this.store.pushPayload('subscriber', subscriberResponse)
            var subscriber = this.store.recordForId('subscriber', response.users.id)

            this.get('session').set('currentUser', this.store.getById('user', response.users.id))
            this.incrementProperty('model.user.statistics.subscribers')
            this.get('model.subscribers').addObject(subscriber)
          })
      },

      unsubscribe: function() {
        var user = this.get('model.user')
        Ember.$.ajax({
          url: config.host + '/v1/users/' + user.get('username') + '/unsubscribe',
          type: 'post',
          context: this
        })
          .then(function(response) {
            var user = this.store.recordForId('user', response.users.id)
            this.store.pushPayload('user', response)
            var subscriberResponse = response
            subscriberResponse.subscribers = subscriberResponse.users
            this.store.pushPayload('subscriber', subscriberResponse)
            var subscriber = this.store.recordForId('subscriber', response.users.id)

            this.get('session').set('currentUser', this.store.getById('user', response.users.id))
            this.decrementProperty('model.user.statistics.subscribers')
            this.get('model.subscribers').removeObject(subscriber)
          })
      },

      addAttachment: function(file) {
        // Create an attachment record
        var attachment = this.store.createRecord('attachment', {
          file: file
        })

        // Add a throbber (placeholder object, to show uploading progress)
        var attachmentList = this.get('attachments')
        var throbber = this.store.createRecord('attachment', { thumbnailUrl: '/img/throbber-100.gif' })
        var throbberIndex = attachmentList.length
        attachmentList.pushObject(throbber)

        // Save the attachment record to the backend
        attachment.save()
          .then(function(attachment) {
            // Replace the throbber with a real record
            attachmentList.replace(throbberIndex, 1, [ attachment ])
          })
      },

      create: function() {
        // Allow/disallow selecting feeds
        var feeds
        if (this.get('selectFeedsOnCreate')) {
          feeds = Ember.$('#sendToSelect').val()
        } else {
          feeds = this.get('model.user.username')
        }

        // Create a post record
        var post = this.store.createRecord('post', {
          body: this.get('body'),
          feeds: feeds
        })

        // Attach the attachments (without throbbers, if any)
        var attachmentList = this.get('attachments')
          .filter(function(item) {
            return !!item.id
          })
        post.get('attachments').pushObjects(attachmentList)

        // Clear the form
        this.set('body', '')
        this.set('attachments', [])
        this.set('isAttachmentsVisible', false)
        this.set('isSendToVisible', false)

        // Save it to the backend
        var that = this
        post.save()
          .then(function(post) {
            var object = that.get('model.posts').findProperty('id', post.get('id'))
            if (!object) {
              that.get('model.posts').unshiftObject(post)
            }
          })
      }
    }
  })
})
<|endoftext|>


<commit_before>import {arrayBufferToBase64} from '../support/function.js'

describe('Selection tool', function () {
    beforeEach(function () {
        cy.visit('/index.php/view/map/?repository=testsrepository&project=selection')
    })

    it('should toggle selection tool', function () {
        cy.get('#button-selectiontool').click()
        cy.get('#selectiontool').should('be.visible')

        cy.get('.selectiontool-unselect').should('have.attr', 'disabled')
        cy.get('.selectiontool-filter').should('have.attr', 'disabled')

        cy.get('#button-selectiontool').click()
        cy.get('#selectiontool').should('not.be.visible')
    })

    it('selects features intersecting a polygon', function () {
        cy.get('#button-selectiontool').click()

        // Activate polygon tool
        cy.get('#selectiontool .digitizing-buttons .dropdown-toggle').first().click()
        cy.get('#selectiontool .digitizing-polygon').click()

        // Select single layer and intersects geom operator
        cy.get('lizmap-selection-tool .selectiontool-layer-list').select('selection_polygon')
        cy.get('lizmap-selection-tool .selection-geom-operator').select('intersects')

        // It should select two features
        cy.get('#map')
            .click(200, 350)
            .click(850, 350)
            .dblclick(550, 650)

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^2/)
        })

        // It should not select any features
        cy.get('#map')
            .click(750, 350)
            .click(700, 400)
            .dblclick(700, 350)

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).not.to.match(/^[0-9]/)
        })
    })

    // TODO : tests other geom operators, unselection...
})

describe('Selection tool connected as user a', function () {
    beforeEach(function () {
        cy.loginAsUserA()
        cy.visit('index.php/view/map/?repository=testsrepository&project=selection')
    })

    it('should select the single point on map which turns yellow', function () {
        cy.get('#button-selectiontool').click()

        // Activate polygon tool
        cy.get('#selectiontool .digitizing-buttons .dropdown-toggle').first().click()
        cy.get('#selectiontool .digitizing-polygon').click()

        // Select single layer and intersects geom operator
        cy.get('lizmap-selection-tool .selectiontool-layer-list').select('selection')
        cy.get('lizmap-selection-tool .selection-geom-operator').select('intersects')

        cy.intercept('*REQUEST=GetMap*',
            { middleware: true },
            (req) => {
                req.on('before:response', (res) => {
                    // force all API responses to not be cached
                    // It is needed when launching tests multiple time in headed mode
                    res.headers['cache-control'] = 'no-store'
                })
            }).as('new-selection')

        cy.get('#map')
            .click(380, 280)
            .click(400, 380)
            .dblclick(500, 380)

        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection_yellow.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^1/)
        })
    })
})

describe('Selection tool connected as admin', function () {
    beforeEach(function () {
        cy.loginAsAdmin()
        cy.visit('index.php/view/map/?repository=testsrepository&project=selection')
    })

    it('selects features intersecting a polygon', function () {
        cy.get('#button-selectiontool').click()

        // Activate polygon tool
        cy.get('#selectiontool .digitizing-buttons .dropdown-toggle').first().click()
        cy.get('#selectiontool .digitizing-polygon').click()

        // Select single layer and intersects geom operator
        cy.get('lizmap-selection-tool .selectiontool-layer-list').select('selection')
        cy.get('lizmap-selection-tool .selection-geom-operator').select('intersects')

        cy.intercept('*REQUEST=GetMap*',
            { middleware: true },
            (req) => {
                req.on('before:response', (res) => {
                    // force all API responses to not be cached
                    // It is needed when launching tests multiple time in headed mode
                    res.headers['cache-control'] = 'no-store'
                })
            }).as('new-selection')

        cy.get('#map')
            .click(380, 280)
            .click(400, 380)
            .dblclick(500, 380)

        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-1.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^1/)
        })

        // Unselect
        cy.get('#selectiontool .selectiontool-unselect').click(true)
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-1.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        // It should select two features
        cy.get('#map')
            .click(200, 250)
            .click(850, 250)
            .dblclick(550, 650)

        // First wait get the old selection
        // TO BE FIXED
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect no selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-2.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-1.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        // UI provides selection results
        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^2/)
        })

        // Second wait no selection
        // TO BE FIXED
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect no selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        // Third wait selection
        // TO BE FIXED
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect no selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-2.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })
    })
})
<commit_msg>e2e: click on #newOlMap not #map<commit_after>import {arrayBufferToBase64} from '../support/function.js'

describe('Selection tool', function () {
    beforeEach(function () {
        cy.visit('/index.php/view/map/?repository=testsrepository&project=selection')
    })

    it('should toggle selection tool', function () {
        cy.get('#button-selectiontool').click()
        cy.get('#selectiontool').should('be.visible')

        cy.get('.selectiontool-unselect').should('have.attr', 'disabled')
        cy.get('.selectiontool-filter').should('have.attr', 'disabled')

        cy.get('#button-selectiontool').click()
        cy.get('#selectiontool').should('not.be.visible')
    })

    it('selects features intersecting a polygon', function () {
        cy.get('#button-selectiontool').click()

        // Activate polygon tool
        cy.get('#selectiontool .digitizing-buttons .dropdown-toggle').first().click()
        cy.get('#selectiontool .digitizing-polygon').click()

        // Select single layer and intersects geom operator
        cy.get('lizmap-selection-tool .selectiontool-layer-list').select('selection_polygon')
        cy.get('lizmap-selection-tool .selection-geom-operator').select('intersects')

        // It should select two features
        cy.get('#newOlMap')
            .click(200, 350)
            .click(850, 350)
            .dblclick(550, 650)

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^2/)
        })

        // It should not select any features
        cy.get('#newOlMap')
            .click(750, 350)
            .click(700, 400)
            .dblclick(700, 350)

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).not.to.match(/^[0-9]/)
        })
    })

    // TODO : tests other geom operators, unselection...
})

describe('Selection tool connected as user a', function () {
    beforeEach(function () {
        cy.loginAsUserA()
        cy.visit('index.php/view/map/?repository=testsrepository&project=selection')
    })

    it('should select the single point on map which turns yellow', function () {
        cy.get('#button-selectiontool').click()

        // Activate polygon tool
        cy.get('#selectiontool .digitizing-buttons .dropdown-toggle').first().click()
        cy.get('#selectiontool .digitizing-polygon').click()

        // Select single layer and intersects geom operator
        cy.get('lizmap-selection-tool .selectiontool-layer-list').select('selection')
        cy.get('lizmap-selection-tool .selection-geom-operator').select('intersects')

        cy.intercept('*REQUEST=GetMap*',
            { middleware: true },
            (req) => {
                req.on('before:response', (res) => {
                    // force all API responses to not be cached
                    // It is needed when launching tests multiple time in headed mode
                    res.headers['cache-control'] = 'no-store'
                })
            }).as('new-selection')

        cy.get('#newOlMap')
            .click(380, 280)
            .click(400, 380)
            .dblclick(500, 380)

        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection_yellow.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^1/)
        })
    })
})

describe('Selection tool connected as admin', function () {
    beforeEach(function () {
        cy.loginAsAdmin()
        cy.visit('index.php/view/map/?repository=testsrepository&project=selection')
    })

    it('selects features intersecting a polygon', function () {
        cy.get('#button-selectiontool').click()

        // Activate polygon tool
        cy.get('#selectiontool .digitizing-buttons .dropdown-toggle').first().click()
        cy.get('#selectiontool .digitizing-polygon').click()

        // Select single layer and intersects geom operator
        cy.get('lizmap-selection-tool .selectiontool-layer-list').select('selection')
        cy.get('lizmap-selection-tool .selection-geom-operator').select('intersects')

        cy.intercept('*REQUEST=GetMap*',
            { middleware: true },
            (req) => {
                req.on('before:response', (res) => {
                    // force all API responses to not be cached
                    // It is needed when launching tests multiple time in headed mode
                    res.headers['cache-control'] = 'no-store'
                })
            }).as('new-selection')

        cy.get('#map')
            .click(380, 280)
            .click(400, 380)
            .dblclick(500, 380)

        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-1.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^1/)
        })

        // Unselect
        cy.get('#selectiontool .selectiontool-unselect').click(true)
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-1.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        // It should select two features
        cy.get('#map')
            .click(200, 250)
            .click(850, 250)
            .dblclick(550, 650)

        // First wait get the old selection
        // TO BE FIXED
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect no selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-2.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-1.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        // UI provides selection results
        cy.get('.selectiontool-results').should(($div) => {
            const text = $div.text()

            expect(text).to.match(/^2/)
        })

        // Second wait no selection
        // TO BE FIXED
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect no selection in yellow').to.equal(responseBodyAsBase64)
            })
        })

        // Third wait selection
        // TO BE FIXED
        cy.wait('@new-selection').should(({ request, response }) => {
            const responseBodyAsBase64 = arrayBufferToBase64(response.body)

            cy.fixture('images/selection-admin-0.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect no selection in yellow').to.not.equal(responseBodyAsBase64)
            })

            cy.fixture('images/selection-admin-2.png').then((image) => {
                // image encoded as base64
                expect(image, 'expect selection in yellow').to.equal(responseBodyAsBase64)
            })
        })
    })
})
<|endoftext|>


<commit_before>app.controller('scheduleController', ["$scope", function($scope) {
	$scope.schedules = [];

	// pagination & search variables
	$scope.pagination = {
		filteredSchedules: [],
		currentPage: -1,
		pageSize: 10,
		numPages: 5,
		search: "",
		searching: false
	};

	$scope.getAllSchedules = function() {
		scheduleService.all()
			.then(function (result) {
				if (result.data.status === 200) {
					$scope.scheduls = result.data.data;
					$scope.switchScheduleArray();
				}
			});

	};
	$scope.switchScheduleArray = function() {
		if($scope.pagination.search !== undefined && $scope.pagination.search !== "") {
			$scope.pagination.searching = true;
			$scope.pagination.currentPage = -1;
			$scope.pagination.filteredSchedules = $scope.schedules;
		} else {
			$scope.pagination.searching = false;
			var begin = ($scope.pagination.currentPage - 1) * $scope.pagination.pageSize;
			var end = begin + $scope.pagination.pageSize;
			$scope.pagination.filteredSchedules = $scope.schedules.slice(begin, end);
		}
	};

	if ($scope.schedules.length === 0) {
		$scope.schedules = $scope.getAllSchedules();
		$scope.pagination.currentPage = 1;
	}
}]);

<commit_msg>added schedule service to controller https://app.asana.com/0/84113659891109/94393680990011<commit_after>app.controller('scheduleController', ["$scope", "scheduleService", function($scope, scheduleService) {
	$scope.schedules = [];

	// pagination & search variables
	$scope.pagination = {
		filteredSchedules: [],
		currentPage: -1,
		pageSize: 10,
		numPages: 5,
		search: "",
		searching: false
	};

	$scope.getAllSchedules = function() {
		scheduleService.all()
			.then(function (result) {
				if (result.data.status === 200) {
					$scope.scheduls = result.data.data;
					$scope.switchScheduleArray();
				}
			});

	};
	$scope.switchScheduleArray = function() {
		if($scope.pagination.search !== undefined && $scope.pagination.search !== "") {
			$scope.pagination.searching = true;
			$scope.pagination.currentPage = -1;
			$scope.pagination.filteredSchedules = $scope.schedules;
		} else {
			$scope.pagination.searching = false;
			var begin = ($scope.pagination.currentPage - 1) * $scope.pagination.pageSize;
			var end = begin + $scope.pagination.pageSize;
			$scope.pagination.filteredSchedules = $scope.schedules.slice(begin, end);
		}
	};

	if ($scope.schedules.length === 0) {
		$scope.schedules = $scope.getAllSchedules();
		$scope.pagination.currentPage = 1;
	}
}]);

<|endoftext|>


<commit_before>import {getBrowserLanguage} from '../base/utils'
import $ from 'clappr-zepto'
import CorePlugin from '../base/core_plugin'

/**
 * The internationalization (i18n) plugin
 * @class Strings
 * @constructor
 * @extends CorePlugin
 * @module plugins
 */
export default class Strings extends CorePlugin {
  get name() {return 'strings'}

  constructor(core) {
    super(core)
    this._initializeMessages()
  }
  /**
   * Gets a translated string for the given key.
   * @method t
   * @param {String} key the key to all messages
   * @return {String} translated label
   */
  t(key) {
    const lang = this._language()
    const i18n = lang && this._messages[lang] || this._messages['en']
    return i18n[key] || key
  }

  _language() {return this.core.options.language || getBrowserLanguage()}

  _initializeMessages() {
    const defaultMessages = {
      'en': {
        'live': 'live',
        'back_to_live': 'back to live',
        'disabled': 'Disabled',
        'playback_not_supported': 'Your browser does not support the playback of this video. Please try using a different browser.'
      },
      'pt': {
        'live': 'ao vivo',
        'back_to_live': 'voltar para o ao vivo',
        'disabled': 'Desativado',
        'playback_not_supported': 'Seu navegador não supporta a reprodução deste video. Por favor, tente usar um navegador diferente.'
      },
      'es': {
        'live': 'vivo',
        'back_to_live': 'volver en vivo',
        'disabled': 'Discapacitado',
        'playback_not_supported': 'Su navegador no soporta la reproducción de un video. Por favor, trate de usar un navegador diferente.'
      },
      'ru': {
        'live': 'прямой эфир',
        'back_to_live': 'к прямому эфиру',
        'disabled': 'Отключено',
        'playback_not_supported': 'Ваш браузер не поддерживает воспроизведение этого видео. Пожалуйста, попробуйте другой браузер.'
      },
      'fr': {
        'live': 'en direct',
        'disabled': 'Désactivé',
        'back_to_live': 'retour au direct',
        'playback_not_supported': 'Votre navigateur ne supporte pas la lecture de cette vidéo. Merci de tenter sur un autre navigateur.'
      },
      'tr': {
        'live': 'canlı',
        'back_to_live': 'canlı yayına dön',
        'disabled': 'Engelli',
        'playback_not_supported': 'Tarayıcınız bu videoyu oynatma desteğine sahip değil. Lütfen farklı bir tarayıcı ile deneyin.',
      }
    }
    const strings = this.core.options.strings || {}
    this._messages = Object.keys(defaultMessages).reduce((messages, lang) => {
      messages[lang] = $.extend({}, defaultMessages[lang], strings[lang])
      return messages
    }, {})

    this._messages['pt-BR'] = this._messages['pt']
    this._messages['en-US'] = this._messages['en']
    this._messages['es-419'] = this._messages['es']
    this._messages['fr-FR'] = this._messages['fr']
    this._messages['tr-TR'] = this._messages['tr']
  }
}
<commit_msg>Update strings.js<commit_after>import {getBrowserLanguage} from '../base/utils'
import $ from 'clappr-zepto'
import CorePlugin from '../base/core_plugin'

/**
 * The internationalization (i18n) plugin
 * @class Strings
 * @constructor
 * @extends CorePlugin
 * @module plugins
 */
export default class Strings extends CorePlugin {
  get name() {return 'strings'}

  constructor(core) {
    super(core)
    this._initializeMessages()
  }
  /**
   * Gets a translated string for the given key.
   * @method t
   * @param {String} key the key to all messages
   * @return {String} translated label
   */
  t(key) {
    const lang = this._language()
    const i18n = lang && this._messages[lang] || this._messages['en']
    return i18n[key] || key
  }

  _language() {return this.core.options.language || getBrowserLanguage()}

  _initializeMessages() {
    const defaultMessages = {
      'en': {
        'live': 'live',
        'back_to_live': 'back to live',
        'disabled': 'Disabled',
        'playback_not_supported': 'Your browser does not support the playback of this video. Please try using a different browser.'
      },
      'pt': {
        'live': 'ao vivo',
        'back_to_live': 'voltar para o ao vivo',
        'disabled': 'Desativado',
        'playback_not_supported': 'Seu navegador não supporta a reprodução deste video. Por favor, tente usar um navegador diferente.'
      },
      'es': {
        'live': 'vivo',
        'back_to_live': 'volver en vivo',
        'disabled': 'Discapacitado',
        'playback_not_supported': 'Su navegador no soporta la reproducción de un video. Por favor, trate de usar un navegador diferente.'
      },
      'ru': {
        'live': 'прямой эфир',
        'back_to_live': 'к прямому эфиру',
        'disabled': 'Отключено',
        'playback_not_supported': 'Ваш браузер не поддерживает воспроизведение этого видео. Пожалуйста, попробуйте другой браузер.'
      },
      'fr': {
        'live': 'en direct',
        'disabled': 'Désactivé',
        'back_to_live': 'retour au direct',
        'playback_not_supported': 'Votre navigateur ne supporte pas la lecture de cette vidéo. Merci de tenter sur un autre navigateur.'
      },
      'tr': {
        'live': 'canlı',
        'back_to_live': 'canlı yayına dön',
        'disabled': 'Engelli',
        'playback_not_supported': 'Tarayıcınız bu videoyu oynatma desteğine sahip değil. Lütfen farklı bir tarayıcı ile deneyin.',
      },
      'et': {
        'live': 'Otseülekanne',
        'back_to_live': 'Tagasi otseülekande juurde',
        'disabled': 'Keelatud',
        'playback_not_supported': 'Teie brauser ei toeta selle video taasesitust. Proovige kasutada muud brauserit.'
      }
    }
    const strings = this.core.options.strings || {}
    this._messages = Object.keys(defaultMessages).reduce((messages, lang) => {
      messages[lang] = $.extend({}, defaultMessages[lang], strings[lang])
      return messages
    }, {})

    this._messages['pt-BR'] = this._messages['pt']
    this._messages['en-US'] = this._messages['en']
    this._messages['es-419'] = this._messages['es']
    this._messages['fr-FR'] = this._messages['fr']
    this._messages['tr-TR'] = this._messages['tr']
  }
}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import UserLink from '../UserLink';
import ContentLink from '../ContentLink';
import {timeSince} from 'helpers/timeStampHelpers';
import LikeButton from 'components/LikeButton';
import {connect} from 'react-redux';
import {likeVideoAsync} from 'redux/actions/FeedActions';
import {cleanString} from 'helpers/stringHelpers';
import {Color} from 'constants/css';
import ProfilePic from 'components/ProfilePic';

@connect(
  null,
  {onLikeVideoClick: likeVideoAsync}
)
export default class Heading extends Component {
  render() {
    const {
      type,
      videoTitle,
      action,
      content,
      uploader,
      uploaderPicId,
      targetReplyUploader,
      targetCommentUploader,
      parentContent,
      parentContentId,
      timeStamp,
      onPlayVideoClick,
      attachedVideoShown,
      parentContentLikers = [],
      myId,
      onLikeVideoClick
    } = this.props;

    let targetAction;

    if (!!targetReplyUploader) {
      targetAction = <UserLink user={targetReplyUploader} />'s reply on
    }
    else if (!!targetCommentUploader) {
      targetAction = <UserLink user={targetCommentUploader} />'s comment on
    }
    let userLikedVideo = false;
    for (let i = 0; i < parentContentLikers.length; i++) {
      if (parentContentLikers[i].userId == myId) userLikedVideo = true;
    }

    const pStyle = {fontSize: '1.4rem'};

    switch (type) {
      case 'video':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> uploaded a video: <ContentLink content={parentContent}/> {!!timeStamp ? `(${timeSince(timeStamp)})` : ''}

        )
      case 'comment':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> {action} {targetAction} video: <ContentLink content={parentContent}/> ({timeSince(timeStamp)})

            {attachedVideoShown ?
              <LikeButton
                small
                style={{
                  marginLeft: 'auto',
                  float: 'right'
                }}
                targetLabel="Video"
                liked={userLikedVideo}
                onClick={() => onLikeVideoClick(parentContentId)}
              /> :
               onPlayVideoClick()}
              >
                 Watch

            }

        )
      case 'url':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> shared a link:

                {cleanString(parentContent.title)}

              {!!timeStamp ? ` (${timeSince(timeStamp)})` : ''}

        )
      case 'discussion':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> started a discussion on video: <ContentLink content={parentContent}/>
              {timeStamp ? ` (${timeSince(timeStamp)})` : ''}

        )
      default:
        return
Error

    }
  }
}
<commit_msg>home panel heading padding left adjusted<commit_after>import React, {Component} from 'react';
import UserLink from '../UserLink';
import ContentLink from '../ContentLink';
import {timeSince} from 'helpers/timeStampHelpers';
import LikeButton from 'components/LikeButton';
import {connect} from 'react-redux';
import {likeVideoAsync} from 'redux/actions/FeedActions';
import {cleanString} from 'helpers/stringHelpers';
import {Color} from 'constants/css';
import ProfilePic from 'components/ProfilePic';

@connect(
  null,
  {onLikeVideoClick: likeVideoAsync}
)
export default class Heading extends Component {
  render() {
    const {
      type,
      videoTitle,
      action,
      content,
      uploader,
      uploaderPicId,
      targetReplyUploader,
      targetCommentUploader,
      parentContent,
      parentContentId,
      timeStamp,
      onPlayVideoClick,
      attachedVideoShown,
      parentContentLikers = [],
      myId,
      onLikeVideoClick
    } = this.props;

    let targetAction;

    if (!!targetReplyUploader) {
      targetAction = <UserLink user={targetReplyUploader} />'s reply on
    }
    else if (!!targetCommentUploader) {
      targetAction = <UserLink user={targetCommentUploader} />'s comment on
    }
    let userLikedVideo = false;
    for (let i = 0; i < parentContentLikers.length; i++) {
      if (parentContentLikers[i].userId == myId) userLikedVideo = true;
    }

    const pStyle = {fontSize: '1.4rem'};

    switch (type) {
      case 'video':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> uploaded a video: <ContentLink content={parentContent}/> {!!timeStamp ? `(${timeSince(timeStamp)})` : ''}

        )
      case 'comment':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> {action} {targetAction} video: <ContentLink content={parentContent}/> ({timeSince(timeStamp)})

            {attachedVideoShown ?
              <LikeButton
                small
                style={{
                  marginLeft: 'auto',
                  float: 'right'
                }}
                targetLabel="Video"
                liked={userLikedVideo}
                onClick={() => onLikeVideoClick(parentContentId)}
              /> :
               onPlayVideoClick()}
              >
                 Watch

            }

        )
      case 'url':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> shared a link:

                {cleanString(parentContent.title)}

              {!!timeStamp ? ` (${timeSince(timeStamp)})` : ''}

        )
      case 'discussion':
        return (

            <ProfilePic size='3' userId={uploader.id} profilePicId={uploaderPicId} />

              <UserLink user={uploader} /> started a discussion on video: <ContentLink content={parentContent}/>
              {timeStamp ? ` (${timeSince(timeStamp)})` : ''}

        )
      default:
        return
Error

    }
  }
}
<|endoftext|>